From ce65ef20c6ce0bf1001e655fd82275fdaaca2443 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 24 Nov 2022 21:24:30 -0500 Subject: [PATCH 01/47] Add Python 3.11 end-of-life --- eol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/eol.py b/eol.py index 0960aa47..de9b9502 100644 --- a/eol.py +++ b/eol.py @@ -28,6 +28,7 @@ def date(string, fmt='%Y-%m-%d'): # https://devguide.python.org/ # https://devguide.python.org/devcycle/#devcycle PYTHON_EOL = { + (3, 11): date('2027-10-1'), (3, 10): date('2026-10-01'), (3, 9): date('2025-10-05'), (3, 8): date('2024-10-14'), From 7a3c2bc8a5506cbc703efaca2b670b9d8bab79d4 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 24 Nov 2022 22:47:10 -0500 Subject: [PATCH 02/47] Refactor `nzbToMedia.process` -> `core.processor.nzb.process` --- core/processor/__init__.py | 0 core/processor/nzb.py | 154 +++++++++++++++++++++++++++++++++++++ nzbToMedia.py | 147 +---------------------------------- 3 files changed, 158 insertions(+), 143 deletions(-) create mode 100644 core/processor/__init__.py create mode 100644 core/processor/nzb.py diff --git a/core/processor/__init__.py b/core/processor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/core/processor/nzb.py b/core/processor/nzb.py new file mode 100644 index 00000000..a649654e --- /dev/null +++ b/core/processor/nzb.py @@ -0,0 +1,154 @@ +import datetime + +import core +from core import logger, main_db +from core.auto_process import comics, games, movies, music, tv, books +from core.auto_process.common import ProcessResult +from core.plugins.downloaders.nzb.utils import get_nzoid +from core.plugins.plex import plex_update +from core.user_scripts import external_script +from core.utils import ( + char_replace, + clean_dir, + convert_to_ascii, + extract_files, + update_download_info_status, +) + +try: + text_type = unicode +except NameError: + text_type = str + + +def process(input_directory, input_name=None, status=0, client_agent='manual', download_id=None, input_category=None, failure_link=None): + if core.SAFE_MODE and input_directory == core.NZB_DEFAULT_DIRECTORY: + logger.error( + 'The input directory:[{0}] is the Default Download Directory. Please configure category directories to prevent processing of other media.'.format( + input_directory)) + return ProcessResult( + message='', + status_code=-1, + ) + + if not download_id and client_agent == 'sabnzbd': + download_id = get_nzoid(input_name) + + if client_agent != 'manual' and not core.DOWNLOAD_INFO: + logger.debug('Adding NZB download info for directory {0} to database'.format(input_directory)) + + my_db = main_db.DBConnection() + + input_directory1 = input_directory + input_name1 = input_name + + try: + encoded, input_directory1 = char_replace(input_directory) + encoded, input_name1 = char_replace(input_name) + except Exception: + pass + + control_value_dict = {'input_directory': text_type(input_directory1)} + new_value_dict = { + 'input_name': text_type(input_name1), + 'input_hash': text_type(download_id), + 'input_id': text_type(download_id), + 'client_agent': text_type(client_agent), + 'status': 0, + 'last_update': datetime.date.today().toordinal(), + } + my_db.upsert('downloads', new_value_dict, control_value_dict) + + # auto-detect section + if input_category is None: + input_category = 'UNCAT' + usercat = input_category + section = core.CFG.findsection(input_category).isenabled() + if section is None: + section = core.CFG.findsection('ALL').isenabled() + if section is None: + logger.error( + 'Category:[{0}] is not defined or is not enabled. Please rename it or ensure it is enabled for the appropriate section in your autoProcessMedia.cfg and try again.'.format( + input_category)) + return ProcessResult( + message='', + status_code=-1, + ) + else: + usercat = 'ALL' + + if len(section) > 1: + logger.error( + 'Category:[{0}] is not unique, {1} are using it. Please rename it or disable all other sections using the same category name in your autoProcessMedia.cfg and try again.'.format( + input_category, section.keys())) + return ProcessResult( + message='', + status_code=-1, + ) + + if section: + section_name = section.keys()[0] + logger.info('Auto-detected SECTION:{0}'.format(section_name)) + else: + logger.error('Unable to locate a section with subsection:{0} enabled in your autoProcessMedia.cfg, exiting!'.format( + input_category)) + return ProcessResult( + status_code=-1, + message='', + ) + + cfg = dict(core.CFG[section_name][usercat]) + + extract = int(cfg.get('extract', 0)) + + try: + if int(cfg.get('remote_path')) and not core.REMOTE_PATHS: + logger.error('Remote Path is enabled for {0}:{1} but no Network mount points are defined. Please check your autoProcessMedia.cfg, exiting!'.format( + section_name, input_category)) + return ProcessResult( + status_code=-1, + message='', + ) + except Exception: + logger.error('Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!'.format( + cfg.get('remote_path'), section_name, input_category)) + + input_name, input_directory = convert_to_ascii(input_name, input_directory) + + if extract == 1 and not (status > 0 and core.NOEXTRACTFAILED): + logger.debug('Checking for archives to extract in directory: {0}'.format(input_directory)) + extract_files(input_directory) + + logger.info('Calling {0}:{1} to post-process:{2}'.format(section_name, input_category, input_name)) + + if section_name in ['CouchPotato', 'Radarr', 'Watcher3']: + result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) + elif section_name in ['SickBeard', 'SiCKRAGE', 'NzbDrone', 'Sonarr']: + result = tv.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) + elif section_name in ['HeadPhones', 'Lidarr']: + result = music.process(section_name, input_directory, input_name, status, client_agent, input_category) + elif section_name == 'Mylar': + result = comics.process(section_name, input_directory, input_name, status, client_agent, input_category) + elif section_name == 'Gamez': + result = games.process(section_name, input_directory, input_name, status, client_agent, input_category) + elif section_name == 'LazyLibrarian': + result = books.process(section_name, input_directory, input_name, status, client_agent, input_category) + elif section_name == 'UserScript': + result = external_script(input_directory, input_name, input_category, section[usercat]) + else: + result = ProcessResult( + message='', + status_code=-1, + ) + + plex_update(input_category) + + if result.status_code == 0: + if client_agent != 'manual': + # update download status in our DB + update_download_info_status(input_name, 1) + if section_name not in ['UserScript', 'NzbDrone', 'Sonarr', 'Radarr', 'Lidarr']: + # cleanup our processing folders of any misc unwanted files and empty directories + clean_dir(input_directory, section_name, input_category) + + return result diff --git a/nzbToMedia.py b/nzbToMedia.py index 240033cc..27388cd1 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -722,7 +722,6 @@ from __future__ import ( unicode_literals, ) -import datetime import os import sys @@ -733,16 +732,12 @@ eol.check() cleanup.clean(cleanup.FOLDER_STRUCTURE) import core -from core import logger, main_db -from core.auto_process import comics, games, movies, music, tv, books +from core import logger +from core.processor.nzb import process from core.auto_process.common import ProcessResult -from core.plugins.downloaders.nzb.utils import get_nzoid -from core.plugins.plex import plex_update -from core.user_scripts import external_script from core.utils import ( - char_replace, clean_dir, convert_to_ascii, - extract_files, get_dirs, get_download_info, - update_download_info_status, + get_dirs, + get_download_info, ) try: @@ -751,140 +746,6 @@ except NameError: text_type = str -# post-processing -def process(input_directory, input_name=None, status=0, client_agent='manual', download_id=None, input_category=None, failure_link=None): - if core.SAFE_MODE and input_directory == core.NZB_DEFAULT_DIRECTORY: - logger.error( - 'The input directory:[{0}] is the Default Download Directory. Please configure category directories to prevent processing of other media.'.format( - input_directory)) - return ProcessResult( - message='', - status_code=-1, - ) - - if not download_id and client_agent == 'sabnzbd': - download_id = get_nzoid(input_name) - - if client_agent != 'manual' and not core.DOWNLOAD_INFO: - logger.debug('Adding NZB download info for directory {0} to database'.format(input_directory)) - - my_db = main_db.DBConnection() - - input_directory1 = input_directory - input_name1 = input_name - - try: - encoded, input_directory1 = char_replace(input_directory) - encoded, input_name1 = char_replace(input_name) - except Exception: - pass - - control_value_dict = {'input_directory': text_type(input_directory1)} - new_value_dict = { - 'input_name': text_type(input_name1), - 'input_hash': text_type(download_id), - 'input_id': text_type(download_id), - 'client_agent': text_type(client_agent), - 'status': 0, - 'last_update': datetime.date.today().toordinal(), - } - my_db.upsert('downloads', new_value_dict, control_value_dict) - - # auto-detect section - if input_category is None: - input_category = 'UNCAT' - usercat = input_category - section = core.CFG.findsection(input_category).isenabled() - if section is None: - section = core.CFG.findsection('ALL').isenabled() - if section is None: - logger.error( - 'Category:[{0}] is not defined or is not enabled. Please rename it or ensure it is enabled for the appropriate section in your autoProcessMedia.cfg and try again.'.format( - input_category)) - return ProcessResult( - message='', - status_code=-1, - ) - else: - usercat = 'ALL' - - if len(section) > 1: - logger.error( - 'Category:[{0}] is not unique, {1} are using it. Please rename it or disable all other sections using the same category name in your autoProcessMedia.cfg and try again.'.format( - input_category, section.keys())) - return ProcessResult( - message='', - status_code=-1, - ) - - if section: - section_name = section.keys()[0] - logger.info('Auto-detected SECTION:{0}'.format(section_name)) - else: - logger.error('Unable to locate a section with subsection:{0} enabled in your autoProcessMedia.cfg, exiting!'.format( - input_category)) - return ProcessResult( - status_code=-1, - message='', - ) - - cfg = dict(core.CFG[section_name][usercat]) - - extract = int(cfg.get('extract', 0)) - - try: - if int(cfg.get('remote_path')) and not core.REMOTE_PATHS: - logger.error('Remote Path is enabled for {0}:{1} but no Network mount points are defined. Please check your autoProcessMedia.cfg, exiting!'.format( - section_name, input_category)) - return ProcessResult( - status_code=-1, - message='', - ) - except Exception: - logger.error('Remote Path {0} is not valid for {1}:{2} Please set this to either 0 to disable or 1 to enable!'.format( - cfg.get('remote_path'), section_name, input_category)) - - input_name, input_directory = convert_to_ascii(input_name, input_directory) - - if extract == 1 and not (status > 0 and core.NOEXTRACTFAILED): - logger.debug('Checking for archives to extract in directory: {0}'.format(input_directory)) - extract_files(input_directory) - - logger.info('Calling {0}:{1} to post-process:{2}'.format(section_name, input_category, input_name)) - - if section_name in ['CouchPotato', 'Radarr', 'Watcher3']: - result = movies.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) - elif section_name in ['SickBeard', 'SiCKRAGE', 'NzbDrone', 'Sonarr']: - result = tv.process(section_name, input_directory, input_name, status, client_agent, download_id, input_category, failure_link) - elif section_name in ['HeadPhones', 'Lidarr']: - result = music.process(section_name, input_directory, input_name, status, client_agent, input_category) - elif section_name == 'Mylar': - result = comics.process(section_name, input_directory, input_name, status, client_agent, input_category) - elif section_name == 'Gamez': - result = games.process(section_name, input_directory, input_name, status, client_agent, input_category) - elif section_name == 'LazyLibrarian': - result = books.process(section_name, input_directory, input_name, status, client_agent, input_category) - elif section_name == 'UserScript': - result = external_script(input_directory, input_name, input_category, section[usercat]) - else: - result = ProcessResult( - message='', - status_code=-1, - ) - - plex_update(input_category) - - if result.status_code == 0: - if client_agent != 'manual': - # update download status in our DB - update_download_info_status(input_name, 1) - if section_name not in ['UserScript', 'NzbDrone', 'Sonarr', 'Radarr', 'Lidarr']: - # cleanup our processing folders of any misc unwanted files and empty directories - clean_dir(input_directory, section_name, input_category) - - return result - - def main(args, section=None): # Initialize the config core.initialize(section) From 073b19034ba408cab2bbc7c3d87637251f1a89e1 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 24 Nov 2022 22:59:44 -0500 Subject: [PATCH 03/47] Extract nzbget processing from `nzbToMedia.main` -> `core.processor.nzbget` --- core/processor/nzbget.py | 83 ++++++++++++++++++++++++++++++++++++++++ nzbToMedia.py | 62 +----------------------------- 2 files changed, 85 insertions(+), 60 deletions(-) create mode 100644 core/processor/nzbget.py diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py new file mode 100644 index 00000000..1431d9e2 --- /dev/null +++ b/core/processor/nzbget.py @@ -0,0 +1,83 @@ +import os +import sys + +import core +from core import logger +from core.processor import nzb + + +def process(): + # Check if the script is called from nzbget 11.0 or later + if os.environ['NZBOP_VERSION'][0:5] < '11.0': + logger.error( + 'NZBGet Version {0} is not supported. Please update NZBGet.'.format( + os.environ['NZBOP_VERSION'])) + sys.exit(core.NZBGET_POSTPROCESS_ERROR) + + logger.info('Script triggered from NZBGet Version {0}.'.format( + os.environ['NZBOP_VERSION'])) + + status = 0 + # Check if the script is called from nzbget 13.0 or later + if 'NZBPP_TOTALSTATUS' in os.environ: + if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': + logger.info('Download failed with status {0}.'.format( + os.environ['NZBPP_STATUS'])) + status = 1 + + else: + # Check par status + if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ[ + 'NZBPP_PARSTATUS'] == '4': + logger.warning('Par-repair failed, setting status \'failed\'') + status = 1 + + # Check unpack status + if os.environ['NZBPP_UNPACKSTATUS'] == '1': + logger.warning('Unpack failed, setting status \'failed\'') + status = 1 + + if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ[ + 'NZBPP_PARSTATUS'] == '0': + # Unpack was skipped due to nzb-file properties or due to errors during par-check + + if os.environ['NZBPP_HEALTH'] < 1000: + logger.warning( + 'Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') + logger.info( + 'Please check your Par-check/repair settings for future downloads.') + status = 1 + + else: + logger.info( + 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') + logger.info( + 'Please check your Par-check/repair settings for future downloads.') + + # Check for download_id to pass to CouchPotato + download_id = '' + failure_link = None + if 'NZBPR_COUCHPOTATO' in os.environ: + download_id = os.environ['NZBPR_COUCHPOTATO'] + elif 'NZBPR_DRONE' in os.environ: + download_id = os.environ['NZBPR_DRONE'] + elif 'NZBPR_SONARR' in os.environ: + download_id = os.environ['NZBPR_SONARR'] + elif 'NZBPR_RADARR' in os.environ: + download_id = os.environ['NZBPR_RADARR'] + elif 'NZBPR_LIDARR' in os.environ: + download_id = os.environ['NZBPR_LIDARR'] + if 'NZBPR__DNZB_FAILURE' in os.environ: + failure_link = os.environ['NZBPR__DNZB_FAILURE'] + + # All checks done, now launching the script. + client_agent = 'nzbget' + return nzb.process( + os.environ['NZBPP_DIRECTORY'], + input_name=os.environ['NZBPP_NZBNAME'], + status=status, + client_agent=client_agent, + download_id=download_id, + input_category=os.environ['NZBPP_CATEGORY'], + failure_link=failure_link, + ) diff --git a/nzbToMedia.py b/nzbToMedia.py index 27388cd1..bb88ce0b 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -733,6 +733,7 @@ cleanup.clean(cleanup.FOLDER_STRUCTURE) import core from core import logger +from core.processor import nzbget from core.processor.nzb import process from core.auto_process.common import ProcessResult from core.utils import ( @@ -762,69 +763,10 @@ def main(args, section=None): message='', status_code=0, ) - status = 0 # NZBGet if 'NZBOP_SCRIPTDIR' in os.environ: - # Check if the script is called from nzbget 11.0 or later - if os.environ['NZBOP_VERSION'][0:5] < '11.0': - logger.error('NZBGet Version {0} is not supported. Please update NZBGet.'.format(os.environ['NZBOP_VERSION'])) - sys.exit(core.NZBGET_POSTPROCESS_ERROR) - - logger.info('Script triggered from NZBGet Version {0}.'.format(os.environ['NZBOP_VERSION'])) - - # Check if the script is called from nzbget 13.0 or later - if 'NZBPP_TOTALSTATUS' in os.environ: - if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': - logger.info('Download failed with status {0}.'.format(os.environ['NZBPP_STATUS'])) - status = 1 - - else: - # Check par status - if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_PARSTATUS'] == '4': - logger.warning('Par-repair failed, setting status \'failed\'') - status = 1 - - # Check unpack status - if os.environ['NZBPP_UNPACKSTATUS'] == '1': - logger.warning('Unpack failed, setting status \'failed\'') - status = 1 - - if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ['NZBPP_PARSTATUS'] == '0': - # Unpack was skipped due to nzb-file properties or due to errors during par-check - - if os.environ['NZBPP_HEALTH'] < 1000: - logger.warning( - 'Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') - logger.info('Please check your Par-check/repair settings for future downloads.') - status = 1 - - else: - logger.info( - 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') - logger.info('Please check your Par-check/repair settings for future downloads.') - - # Check for download_id to pass to CouchPotato - download_id = '' - failure_link = None - if 'NZBPR_COUCHPOTATO' in os.environ: - download_id = os.environ['NZBPR_COUCHPOTATO'] - elif 'NZBPR_DRONE' in os.environ: - download_id = os.environ['NZBPR_DRONE'] - elif 'NZBPR_SONARR' in os.environ: - download_id = os.environ['NZBPR_SONARR'] - elif 'NZBPR_RADARR' in os.environ: - download_id = os.environ['NZBPR_RADARR'] - elif 'NZBPR_LIDARR' in os.environ: - download_id = os.environ['NZBPR_LIDARR'] - if 'NZBPR__DNZB_FAILURE' in os.environ: - failure_link = os.environ['NZBPR__DNZB_FAILURE'] - - # All checks done, now launching the script. - client_agent = 'nzbget' - result = process(os.environ['NZBPP_DIRECTORY'], input_name=os.environ['NZBPP_NZBNAME'], status=status, - client_agent=client_agent, download_id=download_id, input_category=os.environ['NZBPP_CATEGORY'], - failure_link=failure_link) + result = nzbget.process() # SABnzbd elif 'SAB_SCRIPT' in os.environ: client_agent = 'sabnzbd' From 58c998712fcca1addc5deb24dddc543844b883f1 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 24 Nov 2022 23:33:17 -0500 Subject: [PATCH 04/47] Extract sabnzb processing from `nzbToMedia.main` -> `core.processor.sabnzbd` --- core/processor/sab.py | 67 +++++++++++++++++++++++++++++++++++++++++++ nzbToMedia.py | 35 +++------------------- 2 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 core/processor/sab.py diff --git a/core/processor/sab.py b/core/processor/sab.py new file mode 100644 index 00000000..3e9f4050 --- /dev/null +++ b/core/processor/sab.py @@ -0,0 +1,67 @@ +import os + +from core import logger +from core.processor import nzb + + +def process_script(): + client_agent = 'sabnzbd' + logger.info('Script triggered from SABnzbd Version {0}.'.format( + os.environ['SAB_VERSION'])) + return nzb.process( + os.environ['SAB_COMPLETE_DIR'], + input_name=os.environ['SAB_FINAL_NAME'], + status=int(os.environ['SAB_PP_STATUS']), + client_agent=client_agent, + download_id=os.environ['SAB_NZO_ID'], + input_category=os.environ['SAB_CAT'], + failure_link=os.environ['SAB_FAILURE_URL'], + ) + + +def process_legacy(args): + # SABnzbd argv: + # 1 The final directory of the job (full path) + # 2 The original name of the NZB file + # 3 Clean version of the job name (no path info and '.nzb' removed) + # 4 Indexer's report number (if supported) + # 5 User-defined category + # 6 Group that the NZB was posted in e.g. alt.binaries.x + # 7 Status of post processing. + # 0 = OK + # 1 = failed verification + # 2 = failed unpack + # 3 = 1+2 + client_agent = 'sabnzbd' + logger.info('Script triggered from SABnzbd') + return nzb.process( + args[1], + input_name=args[2], + status=int(args[7]), + input_category=args[5], + client_agent=client_agent, + download_id='', + ) + + +def process_0717(args): + # SABnzbd argv: + # 1 The final directory of the job (full path) + # 2 The original name of the NZB file + # 3 Clean version of the job name (no path info and '.nzb' removed) + # 4 Indexer's report number (if supported) + # 5 User-defined category + # 6 Group that the NZB was posted in e.g. alt.binaries.x + # 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2 + # 8 Failure URL + client_agent = 'sabnzbd' + logger.info('Script triggered from SABnzbd 0.7.17+') + return nzb.process( + args[1], + input_name=args[2], + status=int(args[7]), + input_category=args[5], + client_agent=client_agent, + download_id='', + failure_link=''.join(args[8:]), + ) diff --git a/nzbToMedia.py b/nzbToMedia.py index bb88ce0b..d561e6d9 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -733,7 +733,7 @@ cleanup.clean(cleanup.FOLDER_STRUCTURE) import core from core import logger -from core.processor import nzbget +from core.processor import nzbget, sab from core.processor.nzb import process from core.auto_process.common import ProcessResult from core.utils import ( @@ -769,40 +769,13 @@ def main(args, section=None): result = nzbget.process() # SABnzbd elif 'SAB_SCRIPT' in os.environ: - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd Version {0}.'.format(os.environ['SAB_VERSION'])) - result = process(os.environ['SAB_COMPLETE_DIR'], input_name=os.environ['SAB_FINAL_NAME'], status=int(os.environ['SAB_PP_STATUS']), - client_agent=client_agent, download_id=os.environ['SAB_NZO_ID'], input_category=os.environ['SAB_CAT'], - failure_link=os.environ['SAB_FAILURE_URL']) + result = sab.process_script() # SABnzbd Pre 0.7.17 elif len(args) == core.SABNZB_NO_OF_ARGUMENTS: - # SABnzbd argv: - # 1 The final directory of the job (full path) - # 2 The original name of the NZB file - # 3 Clean version of the job name (no path info and '.nzb' removed) - # 4 Indexer's report number (if supported) - # 5 User-defined category - # 6 Group that the NZB was posted in e.g. alt.binaries.x - # 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2 - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd') - result = process(args[1], input_name=args[2], status=int(args[7]), input_category=args[5], client_agent=client_agent, - download_id='') + result = sab.process_legacy(args) # SABnzbd 0.7.17+ elif len(args) >= core.SABNZB_0717_NO_OF_ARGUMENTS: - # SABnzbd argv: - # 1 The final directory of the job (full path) - # 2 The original name of the NZB file - # 3 Clean version of the job name (no path info and '.nzb' removed) - # 4 Indexer's report number (if supported) - # 5 User-defined category - # 6 Group that the NZB was posted in e.g. alt.binaries.x - # 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2 - # 8 Failure URL - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd 0.7.17+') - result = process(args[1], input_name=args[2], status=int(args[7]), input_category=args[5], client_agent=client_agent, - download_id='', failure_link=''.join(args[8:])) + result = sab.process_0717(args) # Generic program elif len(args) > 5 and args[5] == 'generic': logger.info('Script triggered from generic program') From 528cbd02cdfb71ec0f1a412e76f1565959d643ea Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Thu, 24 Nov 2022 23:45:09 -0500 Subject: [PATCH 05/47] Extract manual processing from `nzbToMedia.main` -> `core.processor.manual` --- core/processor/manual.py | 72 ++++++++++++++++++++++++++++++++++++++++ nzbToMedia.py | 48 ++------------------------- 2 files changed, 74 insertions(+), 46 deletions(-) create mode 100644 core/processor/manual.py diff --git a/core/processor/manual.py b/core/processor/manual.py new file mode 100644 index 00000000..4227cf3c --- /dev/null +++ b/core/processor/manual.py @@ -0,0 +1,72 @@ +import os + +import core +from core import logger +from core.auto_process.common import ProcessResult +from core.processor import nzb +from core.utils import ( + get_dirs, + get_download_info, +) + +try: + text_type = unicode +except NameError: + text_type = str + + +def process(): + # Perform Manual Post-Processing + logger.warning( + 'Invalid number of arguments received from client, Switching to manual run mode ...') + + # Post-Processing Result + result = ProcessResult( + message='', + status_code=0, + ) + + for section, subsections in core.SECTIONS.items(): + for subsection in subsections: + if not core.CFG[section][subsection].isenabled(): + continue + for dir_name in get_dirs(section, subsection, link='move'): + logger.info( + 'Starting manual run for {0}:{1} - Folder: {2}'.format( + section, subsection, dir_name)) + logger.info( + 'Checking database for download info for {0} ...'.format( + os.path.basename(dir_name))) + + core.DOWNLOAD_INFO = get_download_info( + os.path.basename(dir_name), 0) + if core.DOWNLOAD_INFO: + logger.info('Found download info for {0}, ' + 'setting variables now ...'.format + (os.path.basename(dir_name))) + client_agent = text_type( + core.DOWNLOAD_INFO[0]['client_agent']) or 'manual' + download_id = text_type( + core.DOWNLOAD_INFO[0]['input_id']) or '' + else: + logger.info('Unable to locate download info for {0}, ' + 'continuing to try and process this release ...'.format + (os.path.basename(dir_name))) + client_agent = 'manual' + download_id = '' + + if client_agent and client_agent.lower() not in core.NZB_CLIENTS: + continue + + input_name = os.path.basename(dir_name) + + results = nzb.process(dir_name, input_name, 0, + client_agent=client_agent, + download_id=download_id or None, + input_category=subsection) + if results.status_code != 0: + logger.error( + 'A problem was reported when trying to perform a manual run for {0}:{1}.'.format + (section, subsection)) + result = results + return result diff --git a/nzbToMedia.py b/nzbToMedia.py index d561e6d9..a9b89eee 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -733,18 +733,9 @@ cleanup.clean(cleanup.FOLDER_STRUCTURE) import core from core import logger -from core.processor import nzbget, sab +from core.processor import nzbget, sab, manual from core.processor.nzb import process from core.auto_process.common import ProcessResult -from core.utils import ( - get_dirs, - get_download_info, -) - -try: - text_type = unicode -except NameError: - text_type = str def main(args, section=None): @@ -783,42 +774,7 @@ def main(args, section=None): elif core.NZB_NO_MANUAL: logger.warning('Invalid number of arguments received from client, and no_manual set') else: - # Perform Manual Post-Processing - logger.warning('Invalid number of arguments received from client, Switching to manual run mode ...') - - for section, subsections in core.SECTIONS.items(): - for subsection in subsections: - if not core.CFG[section][subsection].isenabled(): - continue - for dir_name in get_dirs(section, subsection, link='move'): - logger.info('Starting manual run for {0}:{1} - Folder: {2}'.format(section, subsection, dir_name)) - logger.info('Checking database for download info for {0} ...'.format(os.path.basename(dir_name))) - - core.DOWNLOAD_INFO = get_download_info(os.path.basename(dir_name), 0) - if core.DOWNLOAD_INFO: - logger.info('Found download info for {0}, ' - 'setting variables now ...'.format - (os.path.basename(dir_name))) - client_agent = text_type(core.DOWNLOAD_INFO[0]['client_agent']) or 'manual' - download_id = text_type(core.DOWNLOAD_INFO[0]['input_id']) or '' - else: - logger.info('Unable to locate download info for {0}, ' - 'continuing to try and process this release ...'.format - (os.path.basename(dir_name))) - client_agent = 'manual' - download_id = '' - - if client_agent and client_agent.lower() not in core.NZB_CLIENTS: - continue - - input_name = os.path.basename(dir_name) - - results = process(dir_name, input_name, 0, client_agent=client_agent, - download_id=download_id or None, input_category=subsection) - if results.status_code != 0: - logger.error('A problem was reported when trying to perform a manual run for {0}:{1}.'.format - (section, subsection)) - result = results + manual.process() if result.status_code == 0: logger.info('The {0} script completed successfully.'.format(args[0])) From 637020d2bf74d5d9a52ec83c4b49a97484bf1a34 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 00:07:08 -0500 Subject: [PATCH 06/47] Merge legacy sab parsing with 0.7.17+ --- core/__init__.py | 4 --- core/processor/sab.py | 62 ++++++++++++++++--------------------------- nzbToMedia.py | 7 ++--- 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/core/__init__.py b/core/__init__.py index 7906c59b..c0c7debc 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -89,10 +89,6 @@ __version__ = '12.1.10' NZB_CLIENTS = ['sabnzbd', 'nzbget', 'manual'] TORRENT_CLIENTS = ['transmission', 'deluge', 'utorrent', 'rtorrent', 'qbittorrent', 'other', 'manual'] -# sabnzbd constants -SABNZB_NO_OF_ARGUMENTS = 8 -SABNZB_0717_NO_OF_ARGUMENTS = 9 - # sickbeard fork/branch constants FORK_DEFAULT = 'default' FORK_FAILED = 'failed' diff --git a/core/processor/sab.py b/core/processor/sab.py index 3e9f4050..701325e9 100644 --- a/core/processor/sab.py +++ b/core/processor/sab.py @@ -3,6 +3,9 @@ import os from core import logger from core.processor import nzb +# Constants +MINIMUM_ARGUMENTS = 8 + def process_script(): client_agent = 'sabnzbd' @@ -19,49 +22,30 @@ def process_script(): ) -def process_legacy(args): - # SABnzbd argv: - # 1 The final directory of the job (full path) - # 2 The original name of the NZB file - # 3 Clean version of the job name (no path info and '.nzb' removed) - # 4 Indexer's report number (if supported) - # 5 User-defined category - # 6 Group that the NZB was posted in e.g. alt.binaries.x - # 7 Status of post processing. - # 0 = OK - # 1 = failed verification - # 2 = failed unpack - # 3 = 1+2 - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd') +def process(args): + """ + SABnzbd arguments: + 1. The final directory of the job (full path) + 2. The original name of the NZB file + 3. Clean version of the job name (no path info and '.nzb' removed) + 4. Indexer's report number (if supported) + 5. User-defined category + 6. Group that the NZB was posted in e.g. alt.binaries.x + 7. Status of post processing: + 0 = OK + 1 = failed verification + 2 = failed unpack + 3 = 1+2 + 8. Failure URL + """ + version = '0.7.17+' if len(args) > MINIMUM_ARGUMENTS else '' + logger.info('Script triggered from SABnzbd {}'.format(version)) return nzb.process( - args[1], + input_directory=args[1], input_name=args[2], status=int(args[7]), input_category=args[5], - client_agent=client_agent, - download_id='', - ) - - -def process_0717(args): - # SABnzbd argv: - # 1 The final directory of the job (full path) - # 2 The original name of the NZB file - # 3 Clean version of the job name (no path info and '.nzb' removed) - # 4 Indexer's report number (if supported) - # 5 User-defined category - # 6 Group that the NZB was posted in e.g. alt.binaries.x - # 7 Status of post processing. 0 = OK, 1=failed verification, 2=failed unpack, 3=1+2 - # 8 Failure URL - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd 0.7.17+') - return nzb.process( - args[1], - input_name=args[2], - status=int(args[7]), - input_category=args[5], - client_agent=client_agent, + client_agent='sabnzbd', download_id='', failure_link=''.join(args[8:]), ) diff --git a/nzbToMedia.py b/nzbToMedia.py index a9b89eee..e5702999 100755 --- a/nzbToMedia.py +++ b/nzbToMedia.py @@ -762,11 +762,8 @@ def main(args, section=None): elif 'SAB_SCRIPT' in os.environ: result = sab.process_script() # SABnzbd Pre 0.7.17 - elif len(args) == core.SABNZB_NO_OF_ARGUMENTS: - result = sab.process_legacy(args) - # SABnzbd 0.7.17+ - elif len(args) >= core.SABNZB_0717_NO_OF_ARGUMENTS: - result = sab.process_0717(args) + elif len(args) >= sab.MINIMUM_ARGUMENTS: + result = sab.process(args) # Generic program elif len(args) > 5 and args[5] == 'generic': logger.info('Script triggered from generic program') From e8f5dc409a89da353b9b89fc4b1b22f12115418d Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 00:12:03 -0500 Subject: [PATCH 07/47] Standardize processing --- core/processor/sab.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/processor/sab.py b/core/processor/sab.py index 701325e9..b3c57203 100644 --- a/core/processor/sab.py +++ b/core/processor/sab.py @@ -8,14 +8,13 @@ MINIMUM_ARGUMENTS = 8 def process_script(): - client_agent = 'sabnzbd' - logger.info('Script triggered from SABnzbd Version {0}.'.format( - os.environ['SAB_VERSION'])) + version = os.environ['SAB_VERSION'] + logger.info('Script triggered from SABnzbd {0}.'.format(version)) return nzb.process( - os.environ['SAB_COMPLETE_DIR'], + input_directory=os.environ['SAB_COMPLETE_DIR'], input_name=os.environ['SAB_FINAL_NAME'], status=int(os.environ['SAB_PP_STATUS']), - client_agent=client_agent, + client_agent='sabnzbd', download_id=os.environ['SAB_NZO_ID'], input_category=os.environ['SAB_CAT'], failure_link=os.environ['SAB_FAILURE_URL'], From a2b2e4f6200c8211a95d5c25a8b239f8f9103b21 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 09:51:04 -0500 Subject: [PATCH 08/47] Extract download_id parsing from `core.processor.nzbget.process` -> `parse_download_id` --- core/processor/nzbget.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 1431d9e2..9d399a05 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -6,6 +6,22 @@ from core import logger from core.processor import nzb +def parse_download_id(): + # Check for download_id to pass to CouchPotato + download_id = '' + if 'NZBPR_COUCHPOTATO' in os.environ: + download_id = os.environ['NZBPR_COUCHPOTATO'] + elif 'NZBPR_DRONE' in os.environ: + download_id = os.environ['NZBPR_DRONE'] + elif 'NZBPR_SONARR' in os.environ: + download_id = os.environ['NZBPR_SONARR'] + elif 'NZBPR_RADARR' in os.environ: + download_id = os.environ['NZBPR_RADARR'] + elif 'NZBPR_LIDARR' in os.environ: + download_id = os.environ['NZBPR_LIDARR'] + return download_id + + def process(): # Check if the script is called from nzbget 11.0 or later if os.environ['NZBOP_VERSION'][0:5] < '11.0': @@ -54,19 +70,8 @@ def process(): logger.info( 'Please check your Par-check/repair settings for future downloads.') - # Check for download_id to pass to CouchPotato - download_id = '' + download_id = parse_download_id() failure_link = None - if 'NZBPR_COUCHPOTATO' in os.environ: - download_id = os.environ['NZBPR_COUCHPOTATO'] - elif 'NZBPR_DRONE' in os.environ: - download_id = os.environ['NZBPR_DRONE'] - elif 'NZBPR_SONARR' in os.environ: - download_id = os.environ['NZBPR_SONARR'] - elif 'NZBPR_RADARR' in os.environ: - download_id = os.environ['NZBPR_RADARR'] - elif 'NZBPR_LIDARR' in os.environ: - download_id = os.environ['NZBPR_LIDARR'] if 'NZBPR__DNZB_FAILURE' in os.environ: failure_link = os.environ['NZBPR__DNZB_FAILURE'] From 0a8e8fae9f142779131a5b9a5a43eee6cd0ec1d6 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 09:53:50 -0500 Subject: [PATCH 09/47] Extract failure_link parsing from `core.processor.nzbget.process` -> `parse_failure_link` --- core/processor/nzbget.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 9d399a05..594a4580 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -22,6 +22,13 @@ def parse_download_id(): return download_id +def parse_failure_link(): + failure_link = None + if 'NZBPR__DNZB_FAILURE' in os.environ: + failure_link = os.environ['NZBPR__DNZB_FAILURE'] + return failure_link + + def process(): # Check if the script is called from nzbget 11.0 or later if os.environ['NZBOP_VERSION'][0:5] < '11.0': @@ -71,9 +78,7 @@ def process(): 'Please check your Par-check/repair settings for future downloads.') download_id = parse_download_id() - failure_link = None - if 'NZBPR__DNZB_FAILURE' in os.environ: - failure_link = os.environ['NZBPR__DNZB_FAILURE'] + failure_link = parse_failure_link() # All checks done, now launching the script. client_agent = 'nzbget' From de06d45bb0e5633aa0d899d2d1d0fa93a07a912e Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 09:56:07 -0500 Subject: [PATCH 10/47] Extract status parsing from `core.processor.nzbget.process` -> `parse_status` --- core/processor/nzbget.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 594a4580..aec5190e 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -29,17 +29,7 @@ def parse_failure_link(): return failure_link -def process(): - # Check if the script is called from nzbget 11.0 or later - if os.environ['NZBOP_VERSION'][0:5] < '11.0': - logger.error( - 'NZBGet Version {0} is not supported. Please update NZBGet.'.format( - os.environ['NZBOP_VERSION'])) - sys.exit(core.NZBGET_POSTPROCESS_ERROR) - - logger.info('Script triggered from NZBGet Version {0}.'.format( - os.environ['NZBOP_VERSION'])) - +def parse_status(): status = 0 # Check if the script is called from nzbget 13.0 or later if 'NZBPP_TOTALSTATUS' in os.environ: @@ -76,7 +66,21 @@ def process(): 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') logger.info( 'Please check your Par-check/repair settings for future downloads.') + return status + +def process(): + # Check if the script is called from nzbget 11.0 or later + if os.environ['NZBOP_VERSION'][0:5] < '11.0': + logger.error( + 'NZBGet Version {0} is not supported. Please update NZBGet.'.format( + os.environ['NZBOP_VERSION'])) + sys.exit(core.NZBGET_POSTPROCESS_ERROR) + + logger.info('Script triggered from NZBGet Version {0}.'.format( + os.environ['NZBOP_VERSION'])) + + status = parse_status() download_id = parse_download_id() failure_link = parse_failure_link() From 49af821bcb3d97683c216a99d538886635c1f218 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 09:58:59 -0500 Subject: [PATCH 11/47] Extract version checks from `core.processor.nzbget.process` -> `check_version` --- core/processor/nzbget.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index aec5190e..735f4ac2 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -69,7 +69,7 @@ def parse_status(): return status -def process(): +def check_version(): # Check if the script is called from nzbget 11.0 or later if os.environ['NZBOP_VERSION'][0:5] < '11.0': logger.error( @@ -80,6 +80,9 @@ def process(): logger.info('Script triggered from NZBGet Version {0}.'.format( os.environ['NZBOP_VERSION'])) + +def process(): + check_version() status = parse_status() download_id = parse_download_id() failure_link = parse_failure_link() From 3e676f89a59cfeb248a060c201970504fdf85a1a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:01:04 -0500 Subject: [PATCH 12/47] Standardize processing --- core/processor/nzbget.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 735f4ac2..69dff6f9 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -86,14 +86,11 @@ def process(): status = parse_status() download_id = parse_download_id() failure_link = parse_failure_link() - - # All checks done, now launching the script. - client_agent = 'nzbget' return nzb.process( - os.environ['NZBPP_DIRECTORY'], + input_directory=os.environ['NZBPP_DIRECTORY'], input_name=os.environ['NZBPP_NZBNAME'], status=status, - client_agent=client_agent, + client_agent='nzbget', download_id=download_id, input_category=os.environ['NZBPP_CATEGORY'], failure_link=failure_link, From 9cc92ddd7b6562cf14a4be567fdf431cb122b845 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:06:10 -0500 Subject: [PATCH 13/47] Streamline `core.processor.nzbget.parse_download_id` --- core/processor/nzbget.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 69dff6f9..a76fbd6e 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -7,19 +7,21 @@ from core.processor import nzb def parse_download_id(): - # Check for download_id to pass to CouchPotato - download_id = '' - if 'NZBPR_COUCHPOTATO' in os.environ: - download_id = os.environ['NZBPR_COUCHPOTATO'] - elif 'NZBPR_DRONE' in os.environ: - download_id = os.environ['NZBPR_DRONE'] - elif 'NZBPR_SONARR' in os.environ: - download_id = os.environ['NZBPR_SONARR'] - elif 'NZBPR_RADARR' in os.environ: - download_id = os.environ['NZBPR_RADARR'] - elif 'NZBPR_LIDARR' in os.environ: - download_id = os.environ['NZBPR_LIDARR'] - return download_id + """Parse nzbget download_id from environment.""" + download_id_keys = [ + 'NZBPR_COUCHPOTATO', + 'NZBPR_DRONE', + 'NZBPR_SONARR', + 'NZBPR_RADARR', + 'NZBPR_LIDARR', + ] + for download_id_key in download_id_keys: + try: + return os.environ[download_id_key] + except KeyError: + pass + else: + return '' def parse_failure_link(): From d11dda8af8caa0b2abdabaf4d104b2c9a1cb4b1d Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:08:30 -0500 Subject: [PATCH 14/47] Streamline `core.processor.nzbget.parse_failure_link` --- core/processor/nzbget.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index a76fbd6e..a8953e6e 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -25,10 +25,8 @@ def parse_download_id(): def parse_failure_link(): - failure_link = None - if 'NZBPR__DNZB_FAILURE' in os.environ: - failure_link = os.environ['NZBPR__DNZB_FAILURE'] - return failure_link + """Parse nzbget failure_link from environment.""" + return os.environ.get('NZBPR__DNZB_FAILURE') def parse_status(): From d7c6a8e1cc86cd1c5c198dfb9c54282803e0fec5 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:12:36 -0500 Subject: [PATCH 15/47] Streamline `core.processor.nzbget.check_version` --- core/processor/nzbget.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index a8953e6e..b94378b5 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -70,15 +70,13 @@ def parse_status(): def check_version(): + """Check nzbget version and if version is unsupported, exit.""" + version = os.environ['NZBOP_VERSION'] # Check if the script is called from nzbget 11.0 or later - if os.environ['NZBOP_VERSION'][0:5] < '11.0': - logger.error( - 'NZBGet Version {0} is not supported. Please update NZBGet.'.format( - os.environ['NZBOP_VERSION'])) + if version[0:5] < '11.0': + logger.error('NZBGet Version {0} is not supported. Please update NZBGet.'.format(version)) sys.exit(core.NZBGET_POSTPROCESS_ERROR) - - logger.info('Script triggered from NZBGet Version {0}.'.format( - os.environ['NZBOP_VERSION'])) + logger.info('Script triggered from NZBGet Version {0}.'.format(version)) def process(): From fc2ebeb245eb75a139ecd10d4159e93cdb70fecf Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:24:07 -0500 Subject: [PATCH 16/47] Extract total status parsing from `core.processor.nzbget.parse_status` -> `_parse_total_status` --- core/processor/nzbget.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index b94378b5..ac6e79cb 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -29,15 +29,21 @@ def parse_failure_link(): return os.environ.get('NZBPR__DNZB_FAILURE') +def _parse_total_status(): + status = 0 + if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': + logger.info('Download failed with status {0}.'.format( + os.environ['NZBPP_STATUS'])) + status = 1 + return status + return status + + def parse_status(): status = 0 # Check if the script is called from nzbget 13.0 or later if 'NZBPP_TOTALSTATUS' in os.environ: - if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': - logger.info('Download failed with status {0}.'.format( - os.environ['NZBPP_STATUS'])) - status = 1 - + status = _parse_total_status() else: # Check par status if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ[ From e5ea34b569546d19f7bbbd35582362972e39123a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:25:43 -0500 Subject: [PATCH 17/47] Streamline `core.processor.nzbget._parse_total_status` --- core/processor/nzbget.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index ac6e79cb..1d2dd635 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -30,13 +30,12 @@ def parse_failure_link(): def _parse_total_status(): - status = 0 - if not os.environ['NZBPP_TOTALSTATUS'] == 'SUCCESS': - logger.info('Download failed with status {0}.'.format( - os.environ['NZBPP_STATUS'])) - status = 1 - return status - return status + status_summary = os.environ['NZBPP_TOTALSTATUS'] + if status_summary != 'SUCCESS': + status = os.environ['NZBPP_STATUS'] + logger.info('Download failed with status {0}.'.format(status)) + return 1 + return 0 def parse_status(): From ab006eefb2b52f4857586ca381bc6d3ba83361e9 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:30:29 -0500 Subject: [PATCH 18/47] Extract par status parsing from `core.processor.nzbget.parse_status` -> `_parse_par_status` --- core/processor/nzbget.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 1d2dd635..5a9e60fb 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -38,6 +38,16 @@ def _parse_total_status(): return 0 +def _parse_par_status(): + """Parse nzbget par status from environment.""" + status = 0 + if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ[ + 'NZBPP_PARSTATUS'] == '4': + logger.warning('Par-repair failed, setting status \'failed\'') + status = 1 + return status + + def parse_status(): status = 0 # Check if the script is called from nzbget 13.0 or later @@ -45,10 +55,7 @@ def parse_status(): status = _parse_total_status() else: # Check par status - if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ[ - 'NZBPP_PARSTATUS'] == '4': - logger.warning('Par-repair failed, setting status \'failed\'') - status = 1 + par_status = _parse_par_status() # Check unpack status if os.environ['NZBPP_UNPACKSTATUS'] == '1': @@ -71,6 +78,7 @@ def parse_status(): 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') logger.info( 'Please check your Par-check/repair settings for future downloads.') + return par_status or status return status From 8e96d17537e8a336af377adc7dd82f6382f728f6 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:31:19 -0500 Subject: [PATCH 19/47] Streamline `core.processor.nzbget._parse_par_status` --- core/processor/nzbget.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 5a9e60fb..26a65f4e 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -40,12 +40,11 @@ def _parse_total_status(): def _parse_par_status(): """Parse nzbget par status from environment.""" - status = 0 - if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ[ - 'NZBPP_PARSTATUS'] == '4': + par_status = os.environ['NZBPP_PARSTATUS'] + if par_status == '1' or par_status == '4': logger.warning('Par-repair failed, setting status \'failed\'') - status = 1 - return status + return 1 + return 0 def parse_status(): From 11adb220d8c317470d299691647e7fdf5428ba7b Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:33:48 -0500 Subject: [PATCH 20/47] Extract unpack status parsing from `core.processor.nzbget.parse_status` -> `_parse_unpack_status` --- core/processor/nzbget.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 26a65f4e..b11c7065 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -47,6 +47,14 @@ def _parse_par_status(): return 0 +def _parse_unpack_status(): + status = 0 + if os.environ['NZBPP_UNPACKSTATUS'] == '1': + logger.warning('Unpack failed, setting status \'failed\'') + status = 1 + return status + + def parse_status(): status = 0 # Check if the script is called from nzbget 13.0 or later @@ -57,9 +65,7 @@ def parse_status(): par_status = _parse_par_status() # Check unpack status - if os.environ['NZBPP_UNPACKSTATUS'] == '1': - logger.warning('Unpack failed, setting status \'failed\'') - status = 1 + unpack_status = _parse_unpack_status() if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ[ 'NZBPP_PARSTATUS'] == '0': @@ -77,7 +83,7 @@ def parse_status(): 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') logger.info( 'Please check your Par-check/repair settings for future downloads.') - return par_status or status + return par_status or unpack_status or status return status From efee5c722b8e3a74413e1c88a0beff2b03e21706 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:39:46 -0500 Subject: [PATCH 21/47] Extract health status parsing from `core.processor.nzbget.parse_status` -> `_parse_health_status` --- core/processor/nzbget.py | 41 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index b11c7065..7cdd09e3 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -55,6 +55,28 @@ def _parse_unpack_status(): return status +def _parse_health_status(): + """Parse nzbget download health from environment.""" + status = 0 + if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ[ + 'NZBPP_PARSTATUS'] == '0': + # Unpack was skipped due to nzb-file properties or due to errors during par-check + + if os.environ['NZBPP_HEALTH'] < 1000: + logger.warning( + 'Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') + logger.info( + 'Please check your Par-check/repair settings for future downloads.') + status = 1 + + else: + logger.info( + 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') + logger.info( + 'Please check your Par-check/repair settings for future downloads.') + return status + + def parse_status(): status = 0 # Check if the script is called from nzbget 13.0 or later @@ -67,23 +89,10 @@ def parse_status(): # Check unpack status unpack_status = _parse_unpack_status() - if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ[ - 'NZBPP_PARSTATUS'] == '0': - # Unpack was skipped due to nzb-file properties or due to errors during par-check + # Check download health + health_status = _parse_health_status() - if os.environ['NZBPP_HEALTH'] < 1000: - logger.warning( - 'Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') - logger.info( - 'Please check your Par-check/repair settings for future downloads.') - status = 1 - - else: - logger.info( - 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') - logger.info( - 'Please check your Par-check/repair settings for future downloads.') - return par_status or unpack_status or status + return par_status or unpack_status or health_status or status return status From c34159d881361de6be0068dfac06130b7d12521a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:45:13 -0500 Subject: [PATCH 22/47] Streamline `core.processor.nzbget._parse_health_status` --- core/processor/nzbget.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 7cdd09e3..d92043cc 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -58,22 +58,17 @@ def _parse_unpack_status(): def _parse_health_status(): """Parse nzbget download health from environment.""" status = 0 - if os.environ['NZBPP_UNPACKSTATUS'] == '0' and os.environ[ - 'NZBPP_PARSTATUS'] == '0': - # Unpack was skipped due to nzb-file properties or due to errors during par-check - - if os.environ['NZBPP_HEALTH'] < 1000: - logger.warning( - 'Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') - logger.info( - 'Please check your Par-check/repair settings for future downloads.') + unpack_status_value = os.environ['NZBPP_UNPACKSTATUS'] + par_status_value = os.environ['NZBPP_PARSTATUS'] + if unpack_status_value == '0' and par_status_value == '0': + # Unpack was skipped due to nzb-file properties + # or due to errors during par-check + if int(os.environ['NZBPP_HEALTH']) < 1000: + logger.warning('Download health is compromised and Par-check/repair disabled or no .par2 files found. Setting status \'failed\'') status = 1 - else: - logger.info( - 'Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') - logger.info( - 'Please check your Par-check/repair settings for future downloads.') + logger.info('Par-check/repair disabled or no .par2 files found, and Unpack not required. Health is ok so handle as though download successful') + logger.info('Please check your Par-check/repair settings for future downloads.') return status From 7737d0c4bea77596027177683c6994433fb78999 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:46:20 -0500 Subject: [PATCH 23/47] Streamline `core.processor.nzbget._parse_unpack_status` --- core/processor/nzbget.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index d92043cc..5a8358f6 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -48,11 +48,10 @@ def _parse_par_status(): def _parse_unpack_status(): - status = 0 if os.environ['NZBPP_UNPACKSTATUS'] == '1': logger.warning('Unpack failed, setting status \'failed\'') - status = 1 - return status + return 1 + return 0 def _parse_health_status(): From 34236e8960802ec98cbe864a103fc8bcae315379 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Fri, 25 Nov 2022 10:47:40 -0500 Subject: [PATCH 24/47] Streamline `core.processor.nzbget.parse_status` --- core/processor/nzbget.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/core/processor/nzbget.py b/core/processor/nzbget.py index 5a8358f6..049674bc 100644 --- a/core/processor/nzbget.py +++ b/core/processor/nzbget.py @@ -72,21 +72,13 @@ def _parse_health_status(): def parse_status(): - status = 0 - # Check if the script is called from nzbget 13.0 or later - if 'NZBPP_TOTALSTATUS' in os.environ: + if 'NZBPP_TOTALSTATUS' in os.environ: # Called from nzbget 13.0 or later status = _parse_total_status() else: - # Check par status par_status = _parse_par_status() - - # Check unpack status unpack_status = _parse_unpack_status() - - # Check download health health_status = _parse_health_status() - - return par_status or unpack_status or health_status or status + status = par_status or unpack_status or health_status return status From c85ee42874f66b50d070e3046d9681336db45f6e Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Sun, 27 Nov 2022 18:50:49 -0500 Subject: [PATCH 25/47] Add `processor` folder to folder structure --- cleanup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cleanup.py b/cleanup.py index 3724844b..bb4caf98 100644 --- a/cleanup.py +++ b/cleanup.py @@ -25,6 +25,7 @@ FOLDER_STRUCTURE = { 'auto_process', 'extractor', 'plugins', + 'processor', 'utils', ], } From f61c211655baf02fb2db3894b25563e927bb179d Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Tue, 29 Nov 2022 00:44:07 -0500 Subject: [PATCH 26/47] Fix .gitignore for pyd binary files --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9c80798b..3efe07d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ *.cfg !.bumpversion.cfg *.cfg.old -*.pyc -*.pyo +*.py[cod] *.log *.pid *.db From b1cefa94e5d4a16cce2ba6773ab94f0594fdcd5a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 05:59:32 -0500 Subject: [PATCH 27/47] Update vendored windows libs --- libs/win/autocommand/__init__.py | 27 + libs/win/autocommand/autoasync.py | 142 + libs/win/autocommand/autocommand.py | 70 + libs/win/autocommand/automain.py | 59 + libs/win/autocommand/autoparse.py | 333 ++ libs/win/autocommand/errors.py | 23 + libs/win/bin/enver.exe | Bin 93044 -> 107873 bytes libs/win/bin/find-symlinks.exe | Bin 93071 -> 107900 bytes libs/win/bin/gclip.exe | Bin 93056 -> 107885 bytes libs/win/bin/mklink.exe | Bin 93049 -> 107878 bytes libs/win/bin/pclip.exe | Bin 93060 -> 107889 bytes libs/win/bin/xmouse.exe | Bin 93054 -> 107883 bytes libs/win/bugs/environ-api-wierdness.py | 39 + libs/win/bugs/find_target_path.py | 69 + libs/win/bugs/multi_os_libc.py | 21 + libs/win/bugs/vista-symlink-islink-bug.py | 29 + .../wnetaddconnection2-error-on-64-bit.py | 20 + libs/win/importlib_metadata/__init__.py | 17 - libs/win/importlib_metadata/_hooks.py | 148 - libs/win/importlib_metadata/api.py | 146 - .../win/importlib_metadata/docs/changelog.rst | 57 - libs/win/importlib_metadata/docs/conf.py | 180 - libs/win/importlib_metadata/docs/index.rst | 53 - libs/win/importlib_metadata/docs/using.rst | 133 - libs/win/importlib_metadata/tests/test_api.py | 44 - .../win/importlib_metadata/tests/test_main.py | 121 - libs/win/importlib_metadata/tests/test_zip.py | 42 - libs/win/importlib_metadata/version.txt | 1 - libs/win/importlib_resources/__init__.py | 36 + libs/win/importlib_resources/_adapters.py | 170 + libs/win/importlib_resources/_common.py | 207 + libs/win/importlib_resources/_compat.py | 108 + libs/win/importlib_resources/_itertools.py | 35 + libs/win/importlib_resources/_legacy.py | 120 + libs/win/importlib_resources/abc.py | 170 + .../py.typed} | 0 libs/win/importlib_resources/readers.py | 120 + libs/win/importlib_resources/simple.py | 106 + .../tests/__init__.py | 0 libs/win/importlib_resources/tests/_compat.py | 32 + libs/win/importlib_resources/tests/_path.py | 50 + .../tests/data01}/__init__.py | 0 .../tests/data01/binary.file | Bin 0 -> 4 bytes .../tests/data01/subdirectory}/__init__.py | 0 .../tests/data01/subdirectory/binary.file | Bin 0 -> 4 bytes .../tests/data01/utf-16.file | Bin 0 -> 44 bytes .../tests/data01/utf-8.file | 1 + .../tests/data02/__init__.py | 0 .../tests/data02/one/__init__.py | 0 .../tests/data02/one/resource1.txt | 1 + .../tests/data02/two/__init__.py | 0 .../tests/data02/two/resource2.txt | 1 + .../tests/namespacedata01/binary.file | Bin 0 -> 4 bytes .../tests/namespacedata01/utf-16.file | Bin 0 -> 44 bytes .../tests/namespacedata01/utf-8.file | 1 + .../tests/test_compatibilty_files.py | 102 + .../tests/test_contents.py | 43 + .../importlib_resources/tests/test_files.py | 112 + .../importlib_resources/tests/test_open.py | 81 + .../importlib_resources/tests/test_path.py | 64 + .../importlib_resources/tests/test_read.py | 76 + .../importlib_resources/tests/test_reader.py | 133 + .../tests/test_resource.py | 260 ++ .../importlib_resources/tests/update-zips.py | 53 + libs/win/importlib_resources/tests/util.py | 167 + .../tests/zipdata01/__init__.py | 0 .../tests/zipdata01/ziptestdata.zip | Bin 0 -> 876 bytes .../tests/zipdata02/__init__.py | 0 .../tests/zipdata02/ziptestdata.zip | Bin 0 -> 698 bytes libs/win/incubator/replace-file.py | 10 + libs/win/incubator/trace-symlink.py | 22 + libs/win/inflect/__init__.py | 3991 +++++++++++++++++ libs/win/inflect/py.typed | 0 libs/win/jaraco.classes-1.5-py3.6-nspkg.pth | 1 - .../jaraco.collections-1.6.0-py3.7-nspkg.pth | 1 - .../win/jaraco.functools-1.20-py3.6-nspkg.pth | 1 - .../jaraco.structures-1.1.2-py3.6-nspkg.pth | 1 - libs/win/jaraco.text-1.10.1-py3.6-nspkg.pth | 1 - libs/win/jaraco.ui-1.6-py3.6-nspkg.pth | 1 - libs/win/jaraco.windows-3.9.2-py3.7-nspkg.pth | 1 - libs/win/jaraco/classes/ancestry.py | 107 +- libs/win/jaraco/classes/meta.py | 79 +- libs/win/jaraco/classes/properties.py | 217 +- libs/win/jaraco/collections.py | 1574 ++++--- libs/win/jaraco/context.py | 253 ++ libs/win/jaraco/functools.py | 774 ++-- libs/win/jaraco/structures/binary.py | 237 +- libs/win/jaraco/text.py | 452 -- libs/win/jaraco/text/Lorem ipsum.txt | 2 + libs/win/jaraco/text/__init__.py | 622 +++ libs/win/jaraco/text/layouts.py | 25 + libs/win/jaraco/text/show-newlines.py | 33 + libs/win/jaraco/text/strip-prefix.py | 21 + libs/win/jaraco/text/to-dvorak.py | 6 + libs/win/jaraco/text/to-qwerty.py | 6 + libs/win/jaraco/ui/cmdline.py | 115 +- libs/win/jaraco/ui/editor.py | 171 +- libs/win/jaraco/ui/input.py | 36 +- libs/win/jaraco/ui/menu.py | 56 +- libs/win/jaraco/ui/progress.py | 227 +- libs/win/jaraco/windows/api/clipboard.py | 8 +- libs/win/jaraco/windows/api/credential.py | 58 +- libs/win/jaraco/windows/api/environ.py | 6 +- libs/win/jaraco/windows/api/event.py | 23 +- libs/win/jaraco/windows/api/filesystem.py | 307 +- libs/win/jaraco/windows/api/inet.py | 246 +- libs/win/jaraco/windows/api/library.py | 6 +- libs/win/jaraco/windows/api/memory.py | 28 +- libs/win/jaraco/windows/api/message.py | 33 +- libs/win/jaraco/windows/api/net.py | 28 +- libs/win/jaraco/windows/api/power.py | 40 +- libs/win/jaraco/windows/api/privilege.py | 121 +- libs/win/jaraco/windows/api/process.py | 8 +- libs/win/jaraco/windows/api/security.py | 167 +- libs/win/jaraco/windows/api/shell.py | 69 +- libs/win/jaraco/windows/api/system.py | 8 +- libs/win/jaraco/windows/api/user.py | 4 +- libs/win/jaraco/windows/batch.py | 39 + libs/win/jaraco/windows/clipboard.py | 305 +- libs/win/jaraco/windows/cred.py | 14 +- libs/win/jaraco/windows/dpapi.py | 198 +- libs/win/jaraco/windows/environ.py | 398 +- libs/win/jaraco/windows/error.py | 121 +- libs/win/jaraco/windows/eventlog.py | 68 +- .../win/jaraco/windows/filesystem/__init__.py | 726 ++- .../jaraco/windows/filesystem/backports.py | 192 +- libs/win/jaraco/windows/filesystem/change.py | 370 +- libs/win/jaraco/windows/inet.py | 178 +- libs/win/jaraco/windows/lib.py | 24 +- libs/win/jaraco/windows/memory.py | 36 +- libs/win/jaraco/windows/mmap.py | 105 +- libs/win/jaraco/windows/msie.py | 86 +- libs/win/jaraco/windows/msvc.py | 37 + libs/win/jaraco/windows/net.py | 37 +- libs/win/jaraco/windows/power.py | 88 +- libs/win/jaraco/windows/privilege.py | 190 +- libs/win/jaraco/windows/registry.py | 24 +- libs/win/jaraco/windows/reparse.py | 45 +- libs/win/jaraco/windows/security.py | 99 +- libs/win/jaraco/windows/services.py | 417 +- libs/win/jaraco/windows/shell.py | 14 +- libs/win/jaraco/windows/timers.py | 105 +- libs/win/jaraco/windows/timezone.py | 408 +- libs/win/jaraco/windows/ui.py | 4 +- libs/win/jaraco/windows/user.py | 18 +- libs/win/jaraco/windows/util.py | 21 +- libs/win/jaraco/windows/vpn.py | 28 +- libs/win/jaraco/windows/xmouse.py | 120 +- libs/win/more_itertools/__init__.py | 8 +- libs/win/more_itertools/__init__.pyi | 2 + libs/win/more_itertools/more.py | 2766 ++++++++++-- libs/win/more_itertools/more.pyi | 674 +++ libs/win/more_itertools/py.typed | 0 libs/win/more_itertools/recipes.py | 484 +- libs/win/more_itertools/recipes.pyi | 110 + libs/win/more_itertools/tests/test_more.py | 2074 --------- libs/win/more_itertools/tests/test_recipes.py | 616 --- libs/win/{path.py => path/__init__.py} | 1271 +++--- libs/win/path/__init__.pyi | 483 ++ libs/win/path/classes.py | 27 + libs/win/path/classes.pyi | 8 + libs/win/path/masks.py | 85 + libs/win/path/masks.pyi | 5 + libs/win/path/matchers.py | 59 + libs/win/path/matchers.pyi | 28 + libs/win/path/py.typed | 0 libs/win/path/py37compat.py | 125 + libs/win/path/py37compat.pyi | 17 + libs/win/pydantic/__init__.cp37-win_amd64.pyd | Bin 0 -> 34304 bytes libs/win/pydantic/__init__.py | 131 + .../_hypothesis_plugin.cp37-win_amd64.pyd | Bin 0 -> 154112 bytes libs/win/pydantic/_hypothesis_plugin.py | 386 ++ .../annotated_types.cp37-win_amd64.pyd | Bin 0 -> 67072 bytes libs/win/pydantic/annotated_types.py | 72 + .../class_validators.cp37-win_amd64.pyd | Bin 0 -> 183296 bytes libs/win/pydantic/class_validators.py | 342 ++ libs/win/pydantic/color.cp37-win_amd64.pyd | Bin 0 -> 224256 bytes libs/win/pydantic/color.py | 494 ++ libs/win/pydantic/config.cp37-win_amd64.pyd | Bin 0 -> 79360 bytes libs/win/pydantic/config.py | 192 + .../pydantic/dataclasses.cp37-win_amd64.pyd | Bin 0 -> 182272 bytes libs/win/pydantic/dataclasses.py | 479 ++ .../datetime_parse.cp37-win_amd64.pyd | Bin 0 -> 96256 bytes libs/win/pydantic/datetime_parse.py | 248 + .../win/pydantic/decorator.cp37-win_amd64.pyd | Bin 0 -> 134144 bytes libs/win/pydantic/decorator.py | 264 ++ .../pydantic/env_settings.cp37-win_amd64.pyd | Bin 0 -> 169472 bytes libs/win/pydantic/env_settings.py | 346 ++ .../error_wrappers.cp37-win_amd64.pyd | Bin 0 -> 117248 bytes libs/win/pydantic/error_wrappers.py | 162 + libs/win/pydantic/errors.cp37-win_amd64.pyd | Bin 0 -> 184320 bytes libs/win/pydantic/errors.py | 646 +++ libs/win/pydantic/fields.cp37-win_amd64.pyd | Bin 0 -> 391168 bytes libs/win/pydantic/fields.py | 1247 +++++ libs/win/pydantic/generics.py | 364 ++ libs/win/pydantic/json.cp37-win_amd64.pyd | Bin 0 -> 69632 bytes libs/win/pydantic/json.py | 112 + libs/win/pydantic/main.cp37-win_amd64.pyd | Bin 0 -> 385024 bytes libs/win/pydantic/main.py | 1109 +++++ libs/win/pydantic/mypy.cp37-win_amd64.pyd | Bin 0 -> 325632 bytes libs/win/pydantic/mypy.py | 850 ++++ libs/win/pydantic/networks.cp37-win_amd64.pyd | Bin 0 -> 248320 bytes libs/win/pydantic/networks.py | 736 +++ libs/win/pydantic/parse.cp37-win_amd64.pyd | Bin 0 -> 50176 bytes libs/win/pydantic/parse.py | 66 + libs/win/pydantic/py.typed | 0 libs/win/pydantic/schema.cp37-win_amd64.pyd | Bin 0 -> 346624 bytes libs/win/pydantic/schema.py | 1153 +++++ libs/win/pydantic/tools.cp37-win_amd64.pyd | Bin 0 -> 64000 bytes libs/win/pydantic/tools.py | 92 + libs/win/pydantic/types.cp37-win_amd64.pyd | Bin 0 -> 354816 bytes libs/win/pydantic/types.py | 1187 +++++ libs/win/pydantic/typing.cp37-win_amd64.pyd | Bin 0 -> 200704 bytes libs/win/pydantic/typing.py | 602 +++ libs/win/pydantic/utils.cp37-win_amd64.pyd | Bin 0 -> 305152 bytes libs/win/pydantic/utils.py | 841 ++++ .../pydantic/validators.cp37-win_amd64.pyd | Bin 0 -> 265216 bytes libs/win/pydantic/validators.py | 765 ++++ libs/win/pydantic/version.cp37-win_amd64.pyd | Bin 0 -> 53760 bytes libs/win/pydantic/version.py | 38 + libs/win/scripts/watch-changes.py | 35 + libs/win/six.py | 952 ---- libs/win/test_path.py | 1258 ------ libs/win/typing_extensions.py | 2209 +++++++++ libs/win/zipp/__init__.py | 381 ++ libs/win/zipp/py310compat.py | 12 + 226 files changed, 33472 insertions(+), 11882 deletions(-) create mode 100644 libs/win/autocommand/__init__.py create mode 100644 libs/win/autocommand/autoasync.py create mode 100644 libs/win/autocommand/autocommand.py create mode 100644 libs/win/autocommand/automain.py create mode 100644 libs/win/autocommand/autoparse.py create mode 100644 libs/win/autocommand/errors.py create mode 100644 libs/win/bugs/environ-api-wierdness.py create mode 100644 libs/win/bugs/find_target_path.py create mode 100644 libs/win/bugs/multi_os_libc.py create mode 100644 libs/win/bugs/vista-symlink-islink-bug.py create mode 100644 libs/win/bugs/wnetaddconnection2-error-on-64-bit.py delete mode 100644 libs/win/importlib_metadata/__init__.py delete mode 100644 libs/win/importlib_metadata/_hooks.py delete mode 100644 libs/win/importlib_metadata/api.py delete mode 100644 libs/win/importlib_metadata/docs/changelog.rst delete mode 100644 libs/win/importlib_metadata/docs/conf.py delete mode 100644 libs/win/importlib_metadata/docs/index.rst delete mode 100644 libs/win/importlib_metadata/docs/using.rst delete mode 100644 libs/win/importlib_metadata/tests/test_api.py delete mode 100644 libs/win/importlib_metadata/tests/test_main.py delete mode 100644 libs/win/importlib_metadata/tests/test_zip.py delete mode 100644 libs/win/importlib_metadata/version.txt create mode 100644 libs/win/importlib_resources/__init__.py create mode 100644 libs/win/importlib_resources/_adapters.py create mode 100644 libs/win/importlib_resources/_common.py create mode 100644 libs/win/importlib_resources/_compat.py create mode 100644 libs/win/importlib_resources/_itertools.py create mode 100644 libs/win/importlib_resources/_legacy.py create mode 100644 libs/win/importlib_resources/abc.py rename libs/win/{importlib_metadata/docs/__init__.py => importlib_resources/py.typed} (100%) create mode 100644 libs/win/importlib_resources/readers.py create mode 100644 libs/win/importlib_resources/simple.py rename libs/win/{importlib_metadata => importlib_resources}/tests/__init__.py (100%) create mode 100644 libs/win/importlib_resources/tests/_compat.py create mode 100644 libs/win/importlib_resources/tests/_path.py rename libs/win/{importlib_metadata/tests/data => importlib_resources/tests/data01}/__init__.py (100%) create mode 100644 libs/win/importlib_resources/tests/data01/binary.file rename libs/win/{more_itertools/tests => importlib_resources/tests/data01/subdirectory}/__init__.py (100%) create mode 100644 libs/win/importlib_resources/tests/data01/subdirectory/binary.file create mode 100644 libs/win/importlib_resources/tests/data01/utf-16.file create mode 100644 libs/win/importlib_resources/tests/data01/utf-8.file create mode 100644 libs/win/importlib_resources/tests/data02/__init__.py create mode 100644 libs/win/importlib_resources/tests/data02/one/__init__.py create mode 100644 libs/win/importlib_resources/tests/data02/one/resource1.txt create mode 100644 libs/win/importlib_resources/tests/data02/two/__init__.py create mode 100644 libs/win/importlib_resources/tests/data02/two/resource2.txt create mode 100644 libs/win/importlib_resources/tests/namespacedata01/binary.file create mode 100644 libs/win/importlib_resources/tests/namespacedata01/utf-16.file create mode 100644 libs/win/importlib_resources/tests/namespacedata01/utf-8.file create mode 100644 libs/win/importlib_resources/tests/test_compatibilty_files.py create mode 100644 libs/win/importlib_resources/tests/test_contents.py create mode 100644 libs/win/importlib_resources/tests/test_files.py create mode 100644 libs/win/importlib_resources/tests/test_open.py create mode 100644 libs/win/importlib_resources/tests/test_path.py create mode 100644 libs/win/importlib_resources/tests/test_read.py create mode 100644 libs/win/importlib_resources/tests/test_reader.py create mode 100644 libs/win/importlib_resources/tests/test_resource.py create mode 100644 libs/win/importlib_resources/tests/update-zips.py create mode 100644 libs/win/importlib_resources/tests/util.py create mode 100644 libs/win/importlib_resources/tests/zipdata01/__init__.py create mode 100644 libs/win/importlib_resources/tests/zipdata01/ziptestdata.zip create mode 100644 libs/win/importlib_resources/tests/zipdata02/__init__.py create mode 100644 libs/win/importlib_resources/tests/zipdata02/ziptestdata.zip create mode 100644 libs/win/incubator/replace-file.py create mode 100644 libs/win/incubator/trace-symlink.py create mode 100644 libs/win/inflect/__init__.py create mode 100644 libs/win/inflect/py.typed delete mode 100644 libs/win/jaraco.classes-1.5-py3.6-nspkg.pth delete mode 100644 libs/win/jaraco.collections-1.6.0-py3.7-nspkg.pth delete mode 100644 libs/win/jaraco.functools-1.20-py3.6-nspkg.pth delete mode 100644 libs/win/jaraco.structures-1.1.2-py3.6-nspkg.pth delete mode 100644 libs/win/jaraco.text-1.10.1-py3.6-nspkg.pth delete mode 100644 libs/win/jaraco.ui-1.6-py3.6-nspkg.pth delete mode 100644 libs/win/jaraco.windows-3.9.2-py3.7-nspkg.pth create mode 100644 libs/win/jaraco/context.py delete mode 100644 libs/win/jaraco/text.py create mode 100644 libs/win/jaraco/text/Lorem ipsum.txt create mode 100644 libs/win/jaraco/text/__init__.py create mode 100644 libs/win/jaraco/text/layouts.py create mode 100644 libs/win/jaraco/text/show-newlines.py create mode 100644 libs/win/jaraco/text/strip-prefix.py create mode 100644 libs/win/jaraco/text/to-dvorak.py create mode 100644 libs/win/jaraco/text/to-qwerty.py create mode 100644 libs/win/jaraco/windows/batch.py create mode 100644 libs/win/jaraco/windows/msvc.py create mode 100644 libs/win/more_itertools/__init__.pyi create mode 100644 libs/win/more_itertools/more.pyi create mode 100644 libs/win/more_itertools/py.typed create mode 100644 libs/win/more_itertools/recipes.pyi delete mode 100644 libs/win/more_itertools/tests/test_more.py delete mode 100644 libs/win/more_itertools/tests/test_recipes.py rename libs/win/{path.py => path/__init__.py} (53%) create mode 100644 libs/win/path/__init__.pyi create mode 100644 libs/win/path/classes.py create mode 100644 libs/win/path/classes.pyi create mode 100644 libs/win/path/masks.py create mode 100644 libs/win/path/masks.pyi create mode 100644 libs/win/path/matchers.py create mode 100644 libs/win/path/matchers.pyi create mode 100644 libs/win/path/py.typed create mode 100644 libs/win/path/py37compat.py create mode 100644 libs/win/path/py37compat.pyi create mode 100644 libs/win/pydantic/__init__.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/__init__.py create mode 100644 libs/win/pydantic/_hypothesis_plugin.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/_hypothesis_plugin.py create mode 100644 libs/win/pydantic/annotated_types.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/annotated_types.py create mode 100644 libs/win/pydantic/class_validators.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/class_validators.py create mode 100644 libs/win/pydantic/color.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/color.py create mode 100644 libs/win/pydantic/config.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/config.py create mode 100644 libs/win/pydantic/dataclasses.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/dataclasses.py create mode 100644 libs/win/pydantic/datetime_parse.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/datetime_parse.py create mode 100644 libs/win/pydantic/decorator.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/decorator.py create mode 100644 libs/win/pydantic/env_settings.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/env_settings.py create mode 100644 libs/win/pydantic/error_wrappers.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/error_wrappers.py create mode 100644 libs/win/pydantic/errors.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/errors.py create mode 100644 libs/win/pydantic/fields.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/fields.py create mode 100644 libs/win/pydantic/generics.py create mode 100644 libs/win/pydantic/json.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/json.py create mode 100644 libs/win/pydantic/main.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/main.py create mode 100644 libs/win/pydantic/mypy.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/mypy.py create mode 100644 libs/win/pydantic/networks.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/networks.py create mode 100644 libs/win/pydantic/parse.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/parse.py create mode 100644 libs/win/pydantic/py.typed create mode 100644 libs/win/pydantic/schema.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/schema.py create mode 100644 libs/win/pydantic/tools.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/tools.py create mode 100644 libs/win/pydantic/types.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/types.py create mode 100644 libs/win/pydantic/typing.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/typing.py create mode 100644 libs/win/pydantic/utils.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/utils.py create mode 100644 libs/win/pydantic/validators.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/validators.py create mode 100644 libs/win/pydantic/version.cp37-win_amd64.pyd create mode 100644 libs/win/pydantic/version.py create mode 100644 libs/win/scripts/watch-changes.py delete mode 100644 libs/win/six.py delete mode 100644 libs/win/test_path.py create mode 100644 libs/win/typing_extensions.py create mode 100644 libs/win/zipp/__init__.py create mode 100644 libs/win/zipp/py310compat.py diff --git a/libs/win/autocommand/__init__.py b/libs/win/autocommand/__init__.py new file mode 100644 index 00000000..73fbfca6 --- /dev/null +++ b/libs/win/autocommand/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2014-2016 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + +# flake8 flags all these imports as unused, hence the NOQAs everywhere. + +from .automain import automain # NOQA +from .autoparse import autoparse, smart_open # NOQA +from .autocommand import autocommand # NOQA + +try: + from .autoasync import autoasync # NOQA +except ImportError: # pragma: no cover + pass diff --git a/libs/win/autocommand/autoasync.py b/libs/win/autocommand/autoasync.py new file mode 100644 index 00000000..688f7e05 --- /dev/null +++ b/libs/win/autocommand/autoasync.py @@ -0,0 +1,142 @@ +# Copyright 2014-2015 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + +from asyncio import get_event_loop, iscoroutine +from functools import wraps +from inspect import signature + + +async def _run_forever_coro(coro, args, kwargs, loop): + ''' + This helper function launches an async main function that was tagged with + forever=True. There are two possibilities: + + - The function is a normal function, which handles initializing the event + loop, which is then run forever + - The function is a coroutine, which needs to be scheduled in the event + loop, which is then run forever + - There is also the possibility that the function is a normal function + wrapping a coroutine function + + The function is therefore called unconditionally and scheduled in the event + loop if the return value is a coroutine object. + + The reason this is a separate function is to make absolutely sure that all + the objects created are garbage collected after all is said and done; we + do this to ensure that any exceptions raised in the tasks are collected + ASAP. + ''' + + # Personal note: I consider this an antipattern, as it relies on the use of + # unowned resources. The setup function dumps some stuff into the event + # loop where it just whirls in the ether without a well defined owner or + # lifetime. For this reason, there's a good chance I'll remove the + # forever=True feature from autoasync at some point in the future. + thing = coro(*args, **kwargs) + if iscoroutine(thing): + await thing + + +def autoasync(coro=None, *, loop=None, forever=False, pass_loop=False): + ''' + Convert an asyncio coroutine into a function which, when called, is + evaluted in an event loop, and the return value returned. This is intented + to make it easy to write entry points into asyncio coroutines, which + otherwise need to be explictly evaluted with an event loop's + run_until_complete. + + If `loop` is given, it is used as the event loop to run the coro in. If it + is None (the default), the loop is retreived using asyncio.get_event_loop. + This call is defered until the decorated function is called, so that + callers can install custom event loops or event loop policies after + @autoasync is applied. + + If `forever` is True, the loop is run forever after the decorated coroutine + is finished. Use this for servers created with asyncio.start_server and the + like. + + If `pass_loop` is True, the event loop object is passed into the coroutine + as the `loop` kwarg when the wrapper function is called. In this case, the + wrapper function's __signature__ is updated to remove this parameter, so + that autoparse can still be used on it without generating a parameter for + `loop`. + + This coroutine can be called with ( @autoasync(...) ) or without + ( @autoasync ) arguments. + + Examples: + + @autoasync + def get_file(host, port): + reader, writer = yield from asyncio.open_connection(host, port) + data = reader.read() + sys.stdout.write(data.decode()) + + get_file(host, port) + + @autoasync(forever=True, pass_loop=True) + def server(host, port, loop): + yield_from loop.create_server(Proto, host, port) + + server('localhost', 8899) + + ''' + if coro is None: + return lambda c: autoasync( + c, loop=loop, + forever=forever, + pass_loop=pass_loop) + + # The old and new signatures are required to correctly bind the loop + # parameter in 100% of cases, even if it's a positional parameter. + # NOTE: A future release will probably require the loop parameter to be + # a kwonly parameter. + if pass_loop: + old_sig = signature(coro) + new_sig = old_sig.replace(parameters=( + param for name, param in old_sig.parameters.items() + if name != "loop")) + + @wraps(coro) + def autoasync_wrapper(*args, **kwargs): + # Defer the call to get_event_loop so that, if a custom policy is + # installed after the autoasync decorator, it is respected at call time + local_loop = get_event_loop() if loop is None else loop + + # Inject the 'loop' argument. We have to use this signature binding to + # ensure it's injected in the correct place (positional, keyword, etc) + if pass_loop: + bound_args = old_sig.bind_partial() + bound_args.arguments.update( + loop=local_loop, + **new_sig.bind(*args, **kwargs).arguments) + args, kwargs = bound_args.args, bound_args.kwargs + + if forever: + local_loop.create_task(_run_forever_coro( + coro, args, kwargs, local_loop + )) + local_loop.run_forever() + else: + return local_loop.run_until_complete(coro(*args, **kwargs)) + + # Attach the updated signature. This allows 'pass_loop' to be used with + # autoparse + if pass_loop: + autoasync_wrapper.__signature__ = new_sig + + return autoasync_wrapper diff --git a/libs/win/autocommand/autocommand.py b/libs/win/autocommand/autocommand.py new file mode 100644 index 00000000..097e86de --- /dev/null +++ b/libs/win/autocommand/autocommand.py @@ -0,0 +1,70 @@ +# Copyright 2014-2015 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + +from .autoparse import autoparse +from .automain import automain +try: + from .autoasync import autoasync +except ImportError: # pragma: no cover + pass + + +def autocommand( + module, *, + description=None, + epilog=None, + add_nos=False, + parser=None, + loop=None, + forever=False, + pass_loop=False): + + if callable(module): + raise TypeError('autocommand requires a module name argument') + + def autocommand_decorator(func): + # Step 1: if requested, run it all in an asyncio event loop. autoasync + # patches the __signature__ of the decorated function, so that in the + # event that pass_loop is True, the `loop` parameter of the original + # function will *not* be interpreted as a command-line argument by + # autoparse + if loop is not None or forever or pass_loop: + func = autoasync( + func, + loop=None if loop is True else loop, + pass_loop=pass_loop, + forever=forever) + + # Step 2: create parser. We do this second so that the arguments are + # parsed and passed *before* entering the asyncio event loop, if it + # exists. This simplifies the stack trace and ensures errors are + # reported earlier. It also ensures that errors raised during parsing & + # passing are still raised if `forever` is True. + func = autoparse( + func, + description=description, + epilog=epilog, + add_nos=add_nos, + parser=parser) + + # Step 3: call the function automatically if __name__ == '__main__' (or + # if True was provided) + func = automain(module)(func) + + return func + + return autocommand_decorator diff --git a/libs/win/autocommand/automain.py b/libs/win/autocommand/automain.py new file mode 100644 index 00000000..6cc45db6 --- /dev/null +++ b/libs/win/autocommand/automain.py @@ -0,0 +1,59 @@ +# Copyright 2014-2015 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + +import sys +from .errors import AutocommandError + + +class AutomainRequiresModuleError(AutocommandError, TypeError): + pass + + +def automain(module, *, args=(), kwargs=None): + ''' + This decorator automatically invokes a function if the module is being run + as the "__main__" module. Optionally, provide args or kwargs with which to + call the function. If `module` is "__main__", the function is called, and + the program is `sys.exit`ed with the return value. You can also pass `True` + to cause the function to be called unconditionally. If the function is not + called, it is returned unchanged by the decorator. + + Usage: + + @automain(__name__) # Pass __name__ to check __name__=="__main__" + def main(): + ... + + If __name__ is "__main__" here, the main function is called, and then + sys.exit called with the return value. + ''' + + # Check that @automain(...) was called, rather than @automain + if callable(module): + raise AutomainRequiresModuleError(module) + + if module == '__main__' or module is True: + if kwargs is None: + kwargs = {} + + # Use a function definition instead of a lambda for a neater traceback + def automain_decorator(main): + sys.exit(main(*args, **kwargs)) + + return automain_decorator + else: + return lambda main: main diff --git a/libs/win/autocommand/autoparse.py b/libs/win/autocommand/autoparse.py new file mode 100644 index 00000000..0276a3fa --- /dev/null +++ b/libs/win/autocommand/autoparse.py @@ -0,0 +1,333 @@ +# Copyright 2014-2015 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + +import sys +from re import compile as compile_regex +from inspect import signature, getdoc, Parameter +from argparse import ArgumentParser +from contextlib import contextmanager +from functools import wraps +from io import IOBase +from autocommand.errors import AutocommandError + + +_empty = Parameter.empty + + +class AnnotationError(AutocommandError): + '''Annotation error: annotation must be a string, type, or tuple of both''' + + +class PositionalArgError(AutocommandError): + ''' + Postional Arg Error: autocommand can't handle postional-only parameters + ''' + + +class KWArgError(AutocommandError): + '''kwarg Error: autocommand can't handle a **kwargs parameter''' + + +class DocstringError(AutocommandError): + '''Docstring error''' + + +class TooManySplitsError(DocstringError): + ''' + The docstring had too many ---- section splits. Currently we only support + using up to a single split, to split the docstring into description and + epilog parts. + ''' + + +def _get_type_description(annotation): + ''' + Given an annotation, return the (type, description) for the parameter. + If you provide an annotation that is somehow both a string and a callable, + the behavior is undefined. + ''' + if annotation is _empty: + return None, None + elif callable(annotation): + return annotation, None + elif isinstance(annotation, str): + return None, annotation + elif isinstance(annotation, tuple): + try: + arg1, arg2 = annotation + except ValueError as e: + raise AnnotationError(annotation) from e + else: + if callable(arg1) and isinstance(arg2, str): + return arg1, arg2 + elif isinstance(arg1, str) and callable(arg2): + return arg2, arg1 + + raise AnnotationError(annotation) + + +def _add_arguments(param, parser, used_char_args, add_nos): + ''' + Add the argument(s) to an ArgumentParser (using add_argument) for a given + parameter. used_char_args is the set of -short options currently already in + use, and is updated (if necessary) by this function. If add_nos is True, + this will also add an inverse switch for all boolean options. For + instance, for the boolean parameter "verbose", this will create --verbose + and --no-verbose. + ''' + + # Impl note: This function is kept separate from make_parser because it's + # already very long and I wanted to separate out as much as possible into + # its own call scope, to prevent even the possibility of suble mutation + # bugs. + if param.kind is param.POSITIONAL_ONLY: + raise PositionalArgError(param) + elif param.kind is param.VAR_KEYWORD: + raise KWArgError(param) + + # These are the kwargs for the add_argument function. + arg_spec = {} + is_option = False + + # Get the type and default from the annotation. + arg_type, description = _get_type_description(param.annotation) + + # Get the default value + default = param.default + + # If there is no explicit type, and the default is present and not None, + # infer the type from the default. + if arg_type is None and default not in {_empty, None}: + arg_type = type(default) + + # Add default. The presence of a default means this is an option, not an + # argument. + if default is not _empty: + arg_spec['default'] = default + is_option = True + + # Add the type + if arg_type is not None: + # Special case for bool: make it just a --switch + if arg_type is bool: + if not default or default is _empty: + arg_spec['action'] = 'store_true' + else: + arg_spec['action'] = 'store_false' + + # Switches are always options + is_option = True + + # Special case for file types: make it a string type, for filename + elif isinstance(default, IOBase): + arg_spec['type'] = str + + # TODO: special case for list type. + # - How to specificy type of list members? + # - param: [int] + # - param: int =[] + # - action='append' vs nargs='*' + + else: + arg_spec['type'] = arg_type + + # nargs: if the signature includes *args, collect them as trailing CLI + # arguments in a list. *args can't have a default value, so it can never be + # an option. + if param.kind is param.VAR_POSITIONAL: + # TODO: consider depluralizing metavar/name here. + arg_spec['nargs'] = '*' + + # Add description. + if description is not None: + arg_spec['help'] = description + + # Get the --flags + flags = [] + name = param.name + + if is_option: + # Add the first letter as a -short option. + for letter in name[0], name[0].swapcase(): + if letter not in used_char_args: + used_char_args.add(letter) + flags.append('-{}'.format(letter)) + break + + # If the parameter is a --long option, or is a -short option that + # somehow failed to get a flag, add it. + if len(name) > 1 or not flags: + flags.append('--{}'.format(name)) + + arg_spec['dest'] = name + else: + flags.append(name) + + parser.add_argument(*flags, **arg_spec) + + # Create the --no- version for boolean switches + if add_nos and arg_type is bool: + parser.add_argument( + '--no-{}'.format(name), + action='store_const', + dest=name, + const=default if default is not _empty else False) + + +def make_parser(func_sig, description, epilog, add_nos): + ''' + Given the signature of a function, create an ArgumentParser + ''' + parser = ArgumentParser(description=description, epilog=epilog) + + used_char_args = {'h'} + + # Arange the params so that single-character arguments are first. This + # esnures they don't have to get --long versions. sorted is stable, so the + # parameters will otherwise still be in relative order. + params = sorted( + func_sig.parameters.values(), + key=lambda param: len(param.name) > 1) + + for param in params: + _add_arguments(param, parser, used_char_args, add_nos) + + return parser + + +_DOCSTRING_SPLIT = compile_regex(r'\n\s*-{4,}\s*\n') + + +def parse_docstring(docstring): + ''' + Given a docstring, parse it into a description and epilog part + ''' + if docstring is None: + return '', '' + + parts = _DOCSTRING_SPLIT.split(docstring) + + if len(parts) == 1: + return docstring, '' + elif len(parts) == 2: + return parts[0], parts[1] + else: + raise TooManySplitsError() + + +def autoparse( + func=None, *, + description=None, + epilog=None, + add_nos=False, + parser=None): + ''' + This decorator converts a function that takes normal arguments into a + function which takes a single optional argument, argv, parses it using an + argparse.ArgumentParser, and calls the underlying function with the parsed + arguments. If it is not given, sys.argv[1:] is used. This is so that the + function can be used as a setuptools entry point, as well as a normal main + function. sys.argv[1:] is not evaluated until the function is called, to + allow injecting different arguments for testing. + + It uses the argument signature of the function to create an + ArgumentParser. Parameters without defaults become positional parameters, + while parameters *with* defaults become --options. Use annotations to set + the type of the parameter. + + The `desctiption` and `epilog` parameters corrospond to the same respective + argparse parameters. If no description is given, it defaults to the + decorated functions's docstring, if present. + + If add_nos is True, every boolean option (that is, every parameter with a + default of True/False or a type of bool) will have a --no- version created + as well, which inverts the option. For instance, the --verbose option will + have a --no-verbose counterpart. These are not mutually exclusive- + whichever one appears last in the argument list will have precedence. + + If a parser is given, it is used instead of one generated from the function + signature. In this case, no parser is created; instead, the given parser is + used to parse the argv argument. The parser's results' argument names must + match up with the parameter names of the decorated function. + + The decorated function is attached to the result as the `func` attribute, + and the parser is attached as the `parser` attribute. + ''' + + # If @autoparse(...) is used instead of @autoparse + if func is None: + return lambda f: autoparse( + f, description=description, + epilog=epilog, + add_nos=add_nos, + parser=parser) + + func_sig = signature(func) + + docstr_description, docstr_epilog = parse_docstring(getdoc(func)) + + if parser is None: + parser = make_parser( + func_sig, + description or docstr_description, + epilog or docstr_epilog, + add_nos) + + @wraps(func) + def autoparse_wrapper(argv=None): + if argv is None: + argv = sys.argv[1:] + + # Get empty argument binding, to fill with parsed arguments. This + # object does all the heavy lifting of turning named arguments into + # into correctly bound *args and **kwargs. + parsed_args = func_sig.bind_partial() + parsed_args.arguments.update(vars(parser.parse_args(argv))) + + return func(*parsed_args.args, **parsed_args.kwargs) + + # TODO: attach an updated __signature__ to autoparse_wrapper, just in case. + + # Attach the wrapped function and parser, and return the wrapper. + autoparse_wrapper.func = func + autoparse_wrapper.parser = parser + return autoparse_wrapper + + +@contextmanager +def smart_open(filename_or_file, *args, **kwargs): + ''' + This context manager allows you to open a filename, if you want to default + some already-existing file object, like sys.stdout, which shouldn't be + closed at the end of the context. If the filename argument is a str, bytes, + or int, the file object is created via a call to open with the given *args + and **kwargs, sent to the context, and closed at the end of the context, + just like "with open(filename) as f:". If it isn't one of the openable + types, the object simply sent to the context unchanged, and left unclosed + at the end of the context. Example: + + def work_with_file(name=sys.stdout): + with smart_open(name) as f: + # Works correctly if name is a str filename or sys.stdout + print("Some stuff", file=f) + # If it was a filename, f is closed at the end here. + ''' + if isinstance(filename_or_file, (str, bytes, int)): + with open(filename_or_file, *args, **kwargs) as file: + yield file + else: + yield filename_or_file diff --git a/libs/win/autocommand/errors.py b/libs/win/autocommand/errors.py new file mode 100644 index 00000000..25706073 --- /dev/null +++ b/libs/win/autocommand/errors.py @@ -0,0 +1,23 @@ +# Copyright 2014-2016 Nathan West +# +# This file is part of autocommand. +# +# autocommand is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# autocommand is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with autocommand. If not, see . + + +class AutocommandError(Exception): + '''Base class for autocommand exceptions''' + pass + +# Individual modules will define errors specific to that module. diff --git a/libs/win/bin/enver.exe b/libs/win/bin/enver.exe index 3999307b493ff9c780ae1ee04ef0b14b00fe5afb..238225bca28f6bf754db8f4b5cbad1278d16959d 100644 GIT binary patch literal 107873 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 B^`HO% literal 93044 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 diff --git a/libs/win/bin/gclip.exe b/libs/win/bin/gclip.exe index 0c4805e1a7b0eb51fbf9223c3caf9a8befe987e3..b9b6c928da09fa74c0c842155a3cfb5023b062b8 100644 GIT binary patch literal 107885 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 a;H4&bP`VnE>>rd$!r(+E;8M5f_dfv^#w#TN diff --git a/libs/win/bin/mklink.exe b/libs/win/bin/mklink.exe index 5d477820ea38c619f6db3f782e5ec9b2dab7895a..eca54539916570b03da02feea27c7c53527e1e09 100644 GIT binary patch literal 107878 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 VT@6Osj}j$8a1;|zbtzEq`4?mhEqDL` diff --git a/libs/win/bin/pclip.exe b/libs/win/bin/pclip.exe index 3c3e8b48cc0b84dff4a23c07c26e2b5245ec2ae0..9e8bb32c10d38b7109a5655bb59901b67eea5bc0 100644 GIT binary patch literal 107889 zcmeFadw5jU)%ZWjWXKQ_P7p@IO-Bic#!GzTC2^o;U`Ef#M6iOT3W^3JR;&m!0u?p% zBsRlwT6*_=U)$PN-`aca`&I&4Oo$}mA_TOGVl|4FGY%-ACP6Oe_gVYQB=OSsd7tm! zKOUWP_CEWv_S$Q&z1G@m?bKYq&gFKwTzUL&+b&l#Px)7^e*gC$i_gPP-#Fa$KSQ5C zqd6z~{26l=-@e?xblF|EExY+nf9=iR{N`OT|1G!rm&L#7zx|v3(5!3yciy$=*7J*s z3Q99g|N2`u-}Bx$ZBq9C*xa8^dYAO^2Opc%uAYxi`o4O8f6@m$Kd60V(px;=8#jE? zcU1l_Gv$ArdHz5>zj}M^Vrerwg4to0Yte&4T(?($XxpdR>n~EHlUh$lsXeccwCu(ln zD6|qu&L{~br`vimT3W6rLZyD|x97NAR*8=?$q;!-eW9N89V*OmS#LmO8)RN=-?|T7 zt{rVVcKnMzX_-DN2K7nK|I+7{Z_wvs(A(fXpOHQ%vSKoQj?eU|C+3%W^+c?+_-ndx zz;Al~dpct?Kh@2{75k#*)@XA2acxsq#N2A#da88WsW-O7CYT;mmD?E%ZFK z8t6Bk={IDyMa-9VbEma}-gru*E9s5#F}fRvF>=Of?$v(WY8^Pi80lR%UkEk}|F9-I z-Alc)lR#jd2Wr$X%Ev@=u*w}Ps407c%;iVY?izEynzGepLaXyjt0U%f5%Y%fh`F5Cd-aI9KX_B{ z#^8eB`~@rzhlb`}CxlvF0e%lBzjnS*(?V-m5y(uQmoJ3FoShfkAnOs3b@Qc)tx{%+ zo>)@qw|y5KaJd>=w1x)I`K&Cz^nDQC`2u`$PU&J8P!`@M79N1b+tVJ^scOesDp0dVX7|3Kk89B< zZegvI-*lrhR^z~w#;q}*aWp4ZteZu#Svf9an{E4kNDs{dIEAOQ?Wn$1maT5SV*666 z@7P46FE{S#X>-TNIOWXkA+yEol7)DKZcn;uPy_xO1h1Q6KWcp{7C>8ivdFS++sYp& z0B+aKKC7OIZfsduXm(l?{F1vjou?bynT1_ew<6`wEcYF{345FeXIWgVCzi74SNfeL zFW_23V17DHJxDLodJkj_b1b*wDr2wpj+3#_TBI^8-LUQ0DSF_T)^H_**8_c8!&l_R zIHvO7&>AjK`Fpj7$tu53<#%Weqg8&J)-arx4L!7H`~K^FUO(Y2YFf7MVM%?gQr)(1 zEh*Nnb&G%ijITos*4!+BU8KSlJ(0kPm?skWXS}##Po~XVs8?ft5D6TYFR!UD(uM8Q z)t7hmz{!|L54^6vNO!jHe7;0XT02;yg{XRf2jxD1rS{Y`9*m8T7$@x5sjOYwcP3@~ zJNqO>XN_-5f#a&c4%?UG6ma@{kpg;SPi(wyoTLC8!}fKPM+;8Z|1AX|zG~fEQmPBf zo~@g!OXmrhIBSjxl{xI6KgF~4sLL6xI#H@g0>(FZvIXvg&6)y-wC5l}UOYBR6DjH(!pjF|l4>w1nTA4_atZarE^uYf1p`wd!K4)Hg(n%-NUb|Ts3l2wh`MGr9M#njN;7Ye^8-fk1X*V@X?shBc8iT_1U+}Erp7PRO|&x12_pt7yP zzz`#JU3f{vd{GkBy0NIV62f0pTJGX!3TW;?)L`%~+jr}GivP^CecO3+3hBvYH(wp5 z1uLl76G}vdAx2C68${N%eb-Sq>J(!jE`X~spR3qv^{`y(jQf6-w=AZcbDz)~_r&hd zYcFHB_!Vfg^YlRLvg_d%8;=PFVx#k{7ZDQlm089K7f3c zO1gQ}_FV(TAkqXkd6_EcMs})@wtl9!4n|*AXB<5FJyq%^XL@bl zC`E2kC%!{(+!q@balfvcy{TQ&R?>F@Q6|_dx`Qy}ud$e%ecnR@jV)2LLu95$WZ@=P zlxXXROP3?0vuZk1A|VaCojPNvLk;UrDQ>jqW{MX|@evMemTtaY7pj^Z8>6isCizE^ zerTb9Y0Txr#Q1xe58diR%v%>qOq7o-8S5gb-s$RHz-CDF#tIYOaFq2`XIy?}{Omd- zaHdn}FoghLKzda^XInBlas@m_KN^UG&9G=S`mcjuQ0uQV9ysmH*qDKaN+?Dr($M#5 z2nYoY3g=Z0m}8{zw8n_E^_^C!lUXQmYwO*04DcNmD4h(j&31=#I!tr^O&OladR_`K ztSlLjbCXY03g26E;L&^m(^GpELsxur-4LPeAMS?g*(P+PC< zlLp$4Kog1B>e)In{p`9bHnJEb&LqBQ-2!)m6o$fCa_g!tja$&xL^XmJrF+&y6SZZ+ z74|Be;!Jo46P~XooEm4S8hbJ|t{Yq<=Syk`&!`sO1~aI(-hhRv`r8C#q4jQox|mj? z?6=kC&KiY}ebPK1dJo(7(~6XnMVIHwW{Ae{qSfG03v4x8Fp$TZ`*@ zTWvd@`%#*zZ@B#Kcm!`^f=^<=(JKAu#;$YC+qBm~Ue%!*E8f^_Z$K~p9 zu}itEXQ6_Mmed;FRhd1iOzbrhFvD}46nt+AaKQJW=Gn4_i>%FOOB*HBOFv7=^W$e) ze^y!2EQoO!Ix}cZrMGqKDq7N~qk@NJ?vxgpgCP&J2`;aPjp)Ki=Oe@+&Mg#eLUbpz z?;ROEA!0&*+eE_0*5I>d(AU$_RieZ?2F;mm=r+yu(zmHpex(N|+yEQPqwjoc1T{?f z1FA&Zj`YyFz~fUIgx+I#={nsk(%?}?y zw8`SqXKe&C8C)(04x`gf&y)fgM81J+Q99e|h6dNk{`NzGVsF&ksWiMfBEIgjZY(1c zCHQ4n+}p6si+mF^4!<%eKKR zv~n)RJzHG1AS3rhA{ zj|SyCOx3e!dmv{pj9;q1u_~&OU?L{_zgUQZ=5sZoOm7oFp{TLe;mQ3I`8%}RW~Ai@QomQ9 z_r%^5QQIFecUzxg@rXjG{%}?8sQBf2S+BN!{8#FXW5Hzw{9eBzug+-m3kx>-R92tL zYViwa*6rtG)pf`~fx5unSbO>+1Qg-9e(Tzh%LV6G32pseYX)g0B4lb6NG*@l+QDY2 zt#2!94OJpdiztf8&iJL^?SxXvMJviIBe?Wp=B_$8pM@kmrh-MZCu+V@<@x5tV0Z;M zhMIzwAuI1*V;jq0cxRjZ$!`jk53QOZ z7@Kn=c4rKjw+hVLV$W&oH*4#+mUTk7(j*FkHCGH(s{PN}@nne`N4r<7cFYZpTXijb z6mtLZMZ)~I#{JgJs}TH7W4zXzSMtUYhs!!k@MRV)r4T}rEQ^2clbl1st9w63z2ql6zodw@um->qo;Zq<09Trv4SjD< zOlXjgGn|j}G9UAsF;KYD_bA12BE9K}Q8~J?8QWy5SGTt#ADEsp3a8%{hLZG+qf9ud zyCY_2H0j%|=b)sw=&gMotu|Hu2?YBu*BQNOY ztC{Vi%X*qtWcT$u@}Tt|*5>-vSVg{!k;A!RG%W1Y9~cp5I_fOPI8e!P&6G%D#-WIT zEywyZatG_BQh1V>p0G3bpHX14lmiO$u-Hr%v(#$LqctKJ9?=tXN|y?~j;(=Qd^L?d z@w2Q0$m5w!;Bka9hwZJD#h5i`i;-71pDC?pu4RUXGtUS;k=~D^fzEr*RyMn0tn3wg z#1!I}`7}juiv4;}!M+tgj&MSy{rQdFD@zI?Io(XhhJ?&nrRgCUdyq-R&a)6}fz~jO z&zVxejiBLy*p1QJ72Yrw!r5ArpRbX?2O(|xCt-7EG_Y0s&igwnyRLi}f-~&*5{lbZLm6g;Dn*y{t3!OI3~UVY4+7u$C?aCuGFBE;3$ZkcbpW#e*5%VQvv3utW!8yiz z0|W0JaOE;9bq@oQ3U5P?lFhyiSR(126>L%nL1c*7?dT;6BeI4U%T!Y4VCoIEg<>Qv z>PPsWVTaaaHpdQ5!dZY~`V{l15X(X#mK$U zIz^jEzBT9~a;USzx*Foor~qHX3Wf@RiL^x;ua=z_mWWy$H5Vz9t*I8$0Sj%e^7UfyY~?>v!y#7jcj3WIL=n zgB^MVFXg;Im=wB%LOs(fABazqR>n#zJ_H36kj!o^J9Wfe*wPcMtdEVfzB&NwFoAWa z&4N8gAgKo9=H41p7=;}DKtsE%??G=_mEt1`tdqK2J|iRz|ld{G5$!PHB0_Y_)yg*#q9TLLeS( zJs-67t=jtS6Ghrn2Q>=+N^x7u!Au6{GZ2wPUvis4Z2fX11pg}XOhTp-T0#55Bsmve zrYHYUS}gn@(>+=d@N|!ool=?ZA+n&^XC5(MF^<~ECc!#dT~xZ1ojF<>tJo84{t;7V z{bZIH1Lm`ElHprhBc*~Jjv=RT49QXC zer9>_UgTGMQPE=<6Kvn9(gBtpbFb~omG^|lZ2PWA) z_<2F~8BvhDFv{NKw^>pH)=D|#kuyfXlha8tt64V!2>rnmWP*uar#RM%0j3~h z2v_W@F}pAzz{BFBEe}V686w<7K{nX$%aow#ye%rm#Ss%r<<|IlY3%vC>%vuIVkKd* z6NCaSC=sYP7{7Dt!nAb&+s9b;?g2vrr|_-mvd`Dc`LcR(RgV_N#CN5|&OAoiVHb%c z3L;ua)NU5Vm+G?_QIaWD29Xl$-!R2whLmQ4gqK|pIa}YxsASyh6`Ak8Jfqf&$pG6l zM@Tu=`dZ-;9F6WVuPy1_EHoM~C|m9|+f%3WZU$ge-~K{ECCAUy&%gm!))|#{AfQuQ$?q(i1e^Ien&?cc|u$gY$wtArHN+&Wv0CeVZOxZt-n);-MHx`Fr_rLK;McH zJu^zYv0+k}Rb}?Y+~&Qd=nF|S$E)aF^_&;WwL8jslc9@QDW4_I&z(Rz>i`}GgUudJ z588Zu)*7r=nF+g$eNY(TSgRS^Dy(|Dl@DZ&acmk^X2&)LmCK_1lJx}{yxO>RRit7IZ;oH)LZWOv%-9S8Y0OBcM2C>8L6u&c$p2WbI>y)xyMK5rMnnv_l zd10VvVHKdCt=H~mKVQnbZf=&{o<$)$zHa8V_JoRodG78MzknfYU4KhfL^d-) znJn=B^*u=Tat`5YUx}LgBdx~~DlQ&EPl6$^Gt<22U1c0GR#z0d;uj@n4YfwkV2^jw zW7#(Hb`C>U}4qD z1q%DVNM5>3WAH|{<{i$6@2~u_5z|a@FOs^J)T#-axm*HvU4MSyNm%gUeK}_rNvF+7f zs+6}^4=pej7P2f_=BXCaFSWks5rtTKE`k@H+re4p~;%PA=FvX7ms2S z>9P)l86Uzn}nh0ZWuhFAs7~!o3q2lGB{SUDu^u*?~`9Na$a;{sP{N-+r?F+#*%Z7$icPTYBHK}0)5DYxQ)YV{hpi>Ezsw$F+#Y;Jq-4OX zU^vB*2Cbh02K1PCo^jwb5Dn8%Fns6$cZPG5T`i zL#=znbK-=XiP>(wUL}I-4JF}*eX9zB#{T1>#0@3svB93UoY*LSy|LjzZR7reW%(U5 zO2rT?m5#Eths+LT4VZSlqA89Hw=YU30daljoAB7){#F$sS524R2kDA#IVjV zCfqhb*#BXz!GI{l0#Kn>O_)w*EN<<(ovegs4sL6fxmZ~1s-f1#+Z2z*TXsU3)CI7a zvh=PHcZxN9R0e=yka4WSx?9qLj(f)F=CC?r>Z)mR&Cw35Hncj}-4HmXHw~|HbLze_ zH)}2^cQ!3&6aTXPR4p!L_mab(4f#URZ^K8|RqSKthIB>3j6V zX=1%{>xt9NHaL%SBKne;FJ1urRUH)x#C&>Bbpc{j90$~rmGalyv&I3j4t|dFB)0+{kIvjn^A(LIuveHLJS-+xGX8qnk2g5qjSM&r+jM!z?qw3gkJHC^X zAriHoP+vg;F0eu~9%GlCh9S?KB9IqYdHZD<*G_R6wI%&RiZj)!@6XEER30Q|kFv%N z!0`g!0`Td{z)>9=V%%KfN*}VWcrPPyU(Al-NZi}WO0!VrBgqcxCnLt!9%bFUs2q&g z6~2x!;tu>bH^`hDHk_imp;=Bp;{}1E@d-WE3v*(lt;Z?8fzdJAz1G80H_#S4jdmWA zceB&>*)56y3T)r=ykUsWg)(0@nEw9;$5nG?H|zAs@U>m;`)REd~0jI0h} zGkxjd)=&D|XLk7TSIROG&#N7qHIfJfNT1gMG17Irenr=3m}i1MjS>@|^lC zZnb$8`_h$qttU)^%_&zN?$$Mx5*ifWzhM}YQdgyy#P?S0GnyM1-|%~eRz2Lui}vF- z7FD(NGFlj`;!I=OW2fY}aQj9{5J4tXInZjk4Af zm?6udV1rDI^((Rz30eLrl8+gv(@K&VH9ec*xY2~CjRy{ckMY1qRk-lu|CAY!JswUi z6-uSyOZiEUHv0@@d=BHu9z)I!>jHk)Ia+{-{n%QRK4e|aXh=^<-NS^6UJET@ifaNa zkUjzMZQuG$ButNYBp}e=Jh4S$99$^&?0MFmtO&`*go8!b6$|S4M^#dv2E<8}Pngp} zD2=>fOY?M&aN6-lsgDVm7R9u=5P57IZKcoD&4yA1K*IAQ0ML_}q|w4Q?>R%VOwYIE zk?^F8CDl;6PWnrD4$yXoXET{YF<*6fp5e_|o1O$O0>WY{H)h_K+P=+ym&4n0sprhV z>(_{x&i4I^d@zt^PHkV3gfVRhg;@Jexu+;+`<~&$M(Ii|^#77NYn~I>QL0p&&JEs3 z;*OF?V!2;pb_So_RM(iRyfx;9W)R(BRb@?CZ;kn8)O;Uv+7oqJ<3SpU)ZVQ}5;Hpq z_37g1I&i6GtN{ew4gTkM6q+;oqJcx&x1XS>Ipesz8;|gA+?(gp8h#*eft=VF*-tTP zM$CUi+#Pn8*hUh3yFa$(K3DDXJGwzIv1!G3o{R{6kX3$@85Fxpytm6rPEKsqhYVdx|-LB`gyEZ@xGaZjPm(Lif) zVD$2m^gA3AxLeF*3(m!M)LUbFAFxTP6_|E7*U0ShO;m@lGT@uqn3pXLH;vrJ|sDGC1r|+WRcw z9-J%Snc9{cG`k$PQLUkkTy&0xkHJ5#C&tlhA6OOpMzCy`*07r_BQ-2`CFY~A7^#xj zSw^ZLHpV!b7k?-$ddhDo5H`DvUB#)#rTWGX^HV~7)jb^?r3i%?yQ))bRjIw{)nwQqBV|%MtVD3 zEv&su=w*1iiqpGzS=j8A_WxZH!|Y&wq&OtM** zS*C&WeKkA%235qHDH0ecQcRH%=`xD^NfpV@6e&?f zw8jFbv*8pg{tPIgstT>qiO%9|G>j$K>|mve8-6_#ZOvNhjN=t_e%o{RFcviIFcB|F zgvW;xalGN;jCTtU4p86O4&8~4~hrTcP2)!ykcK+W{!17xd`62m=Xfh zzKfwpOCAvTYkOh4H%@^C8&eOtV#IOf9qOqrG2(N3t4mz<%xyGzy=ILcTrMa;sZRvnfG`uYv&h*5gX zl`f@$e|Nt2NnP7_1Jx+fAVtja$3%=3HE6;bUKo`a<&kfT`I#s$2)Z-JJK~~MjR=uB zlBpI9XZL0NND&K_E_=n4Eh&&~qaDr44qFiyLqt);E;h3~HQ)b;j;RaCR zRL_6} zl)^beAqy&Ai`=}e2x6)2ix^XnyW(ZazrlKSGW%j8@3oGkBJKk|IAr!;DXL#j-QziP&ur)JFaz|vM=?27o3~)+gQSN@k(HM40g`rYX_oAS{?ueRbJ{b#gdJ1kQ<%+JMzD^WD0ZuZ=wq?2s+lEuj_kK}Sni zB`g(WO|*xM6S2TnGT1MM6J9w2nS_0n0Rl&29656Iwxvih((eDy-o9zAL*n({Aj1y^6o;x#XPVH2>F7q6l zdG?7Ew^eIAI|IUoMGP>|s(sH3oLWOE7{)tRYiMVM7=1Ie23@^`wTAyuFIQ>}zgI8U zY7I?PHTvdh4d0|L4r%Q8eK%_jKT+RnwT3agr~}4`IV57VvqQ<;gTH`o62%Fl{kLb& zqYW%_Zx35uqCM+kvztdH*T0u--rW~6`%<#BL)x>Ig6Vmw1Szallib4Lw_A696B$Mf zDShcXgT2p5)O_3>9=0D%CWM#SX*f>U4`?A)cc$-f!+xzn$>5Fqm^NJtO?ZW92-N6C z`~JzH%2Q)!XiwqxJ9nLRH@tF{+y@Lw={O6(YIV{SdECYA)fj4l9XHNBpTcJk_`Si9cJ>g1D9F2AxCDREnR5u zLI^9yNt4yWK?CkzZMs7VO;nYT2%(k(TC<2%AS)JBT7K~FvdqE_Z?C#1n2Zpl)pAYp znmyt|vrtfaOS(3=0qRgCuFe!5_D(tyF~8DJ9M-#xQ6W@oc#l)SaPoS;OofLqmUM37 z(kzlwYgr?L#GpFBa#};{(3@TLAhEFjWek6W`+G&vr3eI){u~CvF25*sAPZjawhUt(Yqa>Owf69I$BB9 z{kiV;CJ26e`bD90J=rKN2wgA_b{i`)-1v^xa2%G|*wS1MMq}Pex7LsrhHY5)^sco7;AS~wWsq;g#QAA&>EIf z0a4KG4Y6~EL@i`Nb8K6*iXEcg)q``v!;nqFW$ucrv)E8 zGy$P+TvvkT@ip%;Q?A&!FeFA4bJ0{9wS7NV4a{8!Yq7f&<*PA_%@op9p;dzmDLznL z8Hjt;+_4H7N ztgGx0&CHLoZLSQL^@hrNX{IoKit_I=J26p9kmQo-4BL!1XS}|kcs|JSk+>AQIh&&r zAsqbX#eJ&K2AQ`^?enQ!(L#vHyg0j;egDl^6@weM2M z)>?QHPg2^pxRot(;#O`TVk+W3A*-n8fsG3C^x5jc5h@T>6KRAs1LFy>zYMY*Fy~W^ zI)b#uo|R_`tg-P9tb&~ktltjy<5j#xX9u5_kAUisOc)M;o?zXz1K|*&p+VV{ol>bX zzRH$s^hIK`jlRXP85=uwNayuYmH-6p^XQIr8M4%cJr!l@!_(0%M=%;dZ1Po ze{t#{FYGm`ZpjdqR2MWVFNvSZV`c6|s!E>9Pi>-ve5}ZP{I~v(A8V;!^09dlkkA|Z z?++v?yw*t?k`yNEBx8~k#+uakJ2|(JlMMQu#PuY2^J)#Vd6O-3iJBlaHw^yyEX%e2 zTg17oF#%aO~~Pf5M(rD*bJ0*RiydP20UJ=o*Ui4CnE zTD&$?us=PVWNxgueyBgVHssykv__DX&Tm&wmqg!St3z0bccw2F+Pr!Kc=$yyS?xFa z#;owtOmezEUC817m=(im1>*f&8<>zm^b5;SMU!fNSaEkhhp{L5NU2O9o*gBabp3dC zV9(yqdSh_TRaDk*QR6t*&PFr7{{h52Qa74gAYPZmY(2~&hAX{fLlF<_h6&iibn_RI zCyiiTn6UmJ1*Z#{k2_=!Lw-nlVkv1<9FGH?a!$2HR=?-F6x0*nQFTpEy-G2khiuBy zl+(jeWu4g<_aT?L~>L(slzjtcTf!f zxae`0%L!SzR)LdYP3f?4>_q&O4$qlLE$p1ufxZx8R)@x0b5p!5TAL(il8}gM%o9%C=y?S90bDDsn8483nW;) zZ2M+9f(Y=8fL>+^!RJ?})e$hzZ9y>RFarV_S^NQ8?Dv61%T8^irJr3vA!Dk2&j) zjv+!kl96nElQWrHROZ#=#lc)Si*@lCkoWz0i5m+;Rf|gf+Jg_N5~0$`mGes{$Ih#q zmlK(W@u#FNV`?7(&aiITh_tHDSgv2He=p4lO2#e|&ym^ZkO;Id}3iVM-dpZ+$E8 z?=8t;FFOX~$dRel1dVApSIXTl1j|O(5ma;=??&{11!pN!=A6`?NE(r=6ia?F{W{ z8CtaLCD6^{E}ofOea^=lWmtqLXmtrOjS;5wV8zkJowW)|7knl-~ zobJxZOL%U>a%|4%Y!1 z@8#U3@ZlSpK#Yd=0%F{pSNF(VCXZ zTGi5?3Xu1q++pBY0b^nX{CxP(4v~qBX}dKb!j=faZEGlW-{Eip39y<}iG(>{Bs+1poGD zwfH4w6o>ycVxKx>*DvIOdlD8mF;Pi47Pf3U3!IZ44Bc0= z&~ZSbppzn8h9ls?R~_&&c@BJB!r;Ic%4re+aU50rnpKm%i;)sj)EAtAT4Jj(JbvvE z1(B%l)J-Fc)|-t=SA4#1nhs)dLpQ3p=E55JtSl~5TCg5iEFGB6c=}5m-ZW6+Fyu*2`>_?XrICt40bZ@13{FAp=k9UA|Rad59~dOig(<$!I;+ zBq?V+0J`ycnguoB^W=OProMFeMfThzYOIoU5ip%?JC**sxMK{&L<=DWrT)~YIU*X^ z6T3i9u)wxi=R@cOdB=zV-)8-hM9{cQ?h{FR{Kyp1Ko{)fvXg?+Hg%YQ_{xnF&N+bf zPZ=#nlFZEvr!fq<3o9eb47dKSzCYXo2GV64L_D|F;^~eJ(Ud$+=0wi5u4Apyj8out z?qWi=apaLstZX8WIKA%JfzWABx{G6C#wP34EP4wlCAh1|_PtELQPf2SNW&%Nr!ZM5 zhuD-2v%td-ImQ!zie?jQ9t8&D=3-YzP(qts9cw!ImACdaqUg?OU(+iODvat@6@=+J zQd1jHB9|GG0|#(2|3rt*ARben6HmABS^SlU!`I41MoSnf%T(jHojiT*$_>EQBzU=#8LKdhw#(8ryKLV^Rdzc| z?Nns9L(HV@+a!4g_irWKTz=B>v=Ww(Uw*F2qMY=Dk(p;XYsqxzP3pu`L%03YqIl5R zAZkF!6)c;PN})H8wUGAl*wdZVSDn;6Clzv1i=EUZPHL%>n&PBZI;pWLW!5W%O0z?q zj7KFS>G}2l@KCbv;f>dy@v>izrq1QBlbaQqhgi*2T*r=W zAp<*e@;b+8RL{<0tW0#y1OkH|n05;*JVrQ$=O{Zc7dOU+8<%q}iIf<2tbbM?QSvk~ zd9*EP%?i~Syvq8pmS`eM#umZ}pH$Ox7L%Ru45b6{E!WlbI!;5uVFIyCeC$HA=sn>Y zGRH6dyK3Pylo@uvLfN`}uacLU<7Rq6|FO0sil{wIg=)?N z8h)p+TGMTvr2t>hZQMJ`6+cT`KNsETSz`ak$XzCz{q^_3o7lT*4rL^LF7rq)<(#fq zDZj=bAI|sSo>uYMkmKzo^7wDh=~4 z788;W6=Qk)ez8-*>|&PuY<0(|gIihWMJIkPO38Z!5YN#S(`$hiU=R|p2)SaxvGvK+)4M0QnNjaEj3 zU^WWnFr91f&ekN9qJKeumLD3^-XFpgn6JRKlSOavZv8H-Xvr&s-zgbmt^werZ?oVl zS@c65*8L!duH=fu{1EAvWNgU^x2Yo27V}8M@V+ua<3Viul)x*5BVJUdXa0(*MDf%tG)3ljS_D^(=a_ zqE6N{$$E^ebz$ORGzUAy?7t-AlRG?b9(0i6eCUO%1mRn4@CTmolOGCMG6hSso;%h3 z#zS@IM8%#|9jt1HL_#xdIjMQ1B(_!zN~xLZ(~gXlC}eI5J)iO308-1rPJIwW4`onna;Un1M&H50*B|hULhY@Wy zJ+R}x^b!v-3pA8gOP@!rN1@z`9*2c$4Ie-l$*bMgcW)O~3(1IrCK>mU0f848)7H`i zALjF6jg5f<8-TD=A`_p)ju3oi6pASTFM3=$d|oc+`&k!V5bdrtj-nt2hmBHD-BDPV zxcG0-AU;*LZ`%*Z#X9#M{hUJ!M;Nkmo%MVgVCnk?oce|A{MJL8L84roaoZ(yCRpj4 zde4=TaUB^n@f(^5ZU7*_i-w;;_KuWo;qa1svpG7k_THve_7~c{NZO_Pe5$LEgXgVD zs%SjO9Wj}lkxmX9p3h>11e=Qy%o(wDWD!T~jGn2?23R}b{u6}_`uJ>S=ip4I+}|Wi z1#8;AcfK!Z>=Vw1X3gd|%f!-$gXr2}!6t8ARYh!sy;qDN+zszvVBE!A^+935>r3U(Dr|5*(ks zUruC9g;DDpAOISPjdqeZIQW=gOj`^)$w6Th;;@#m=A28XZjlA4jN;r|@JBS9j6akT zjfx&2!}k42hRayl3fY8s$#=GD@zHm7qzvy@0g>kOiK3?@73f+{4DDtvlDw+duIrd?y9z>yWp3Us za22#qzfe+FhMJbmU@`qtDj1vRaj8+S(+u*~vMpxO5UYa<&6-{Vf;Ne+KCCfW6xM4$)RRT)sH&X*baM+)=uVLD z!Fjs-TrP>s!|NyP3a$+^ikjl4Fn8VVRkgK-+hKEdxX|v1*jpmTbKbzQ)eT|u-H3_t zV_HDLHnE^uEku;aZ@4z}yT06KGnP zE>gwyj=X>Xj!-c#Xg{Z~Ip$?{wTE=_*wdhX+xK0na*JH@ky4>gC%o~F{_sY1<2_8f zKL%^?9qKGJzd7yu>`N60I)^zcL!jDe9e!N>y^1{50SuDh%q@ALg*jgtVnlyojJ3K9_aymj_F{UDrE z<@pe1e$Sb*Nj?c;L&;w_08DsUm^>%f+LgzHf#Y#1|F1y&OB~3`MVwPGNDL+bl-J6{ ziqIdwKw`jv;&ulLEy>z5~v2+3JF)=hbcbIF_B2tfY8gf%!I-FK^A`w_foF68Do0@GIEAr(AUYz^ZN2?+HM zu{#jUN{wTqR$oZrDN>kHj>GMdha^=r(#bq*ZRYwc9B|#F@zMRrWw)ij=O|m8aQkF^?M$cyr>D_1bpFxw~D*EdW*jqy_CH3p@1$qW#ddim^`S z{MYCxHjb69-l}$QXTsF%0fa;2^SS4-P2^Ui^f@?Wdf(uEUG7#F_V;WlSVfI$dKtRI z0j7*=yg13Zmxwtg62^BaJqkn6ekW{L{K4C}S7B%TMJnAuC5Q*niQ75|+lddcefd&e zmD$B**@}(^_yMFNFM`p00t8rEmS@1x0vUzAE?`tE{Y`PpKzszScjfaq*v5U z=iFaJrL^&7lOo0fHBNmJDx~UNqvMRcddY-HwG3YxOxOJ z=5x6EKi_5G_fQwxAcARE_Nc;|iQz%k@ICz(8Nhs<3kqN(_5;zto9bePm$fTfku178 zK6}dn_ZjqGU{JvY24~2n1YTWTV301(TuhM9o+*4%E+!bx#RSuv=1;c&5BC!Mi+vNJ z`E$1tunTb!!)I&WoNbdInM&xRQXzIE@fL21L|>#zd}Z z51np zJm+tal*5ZyN*q!P016a!JC*edV1QrAeFTo49G3AD(Y2Mp-o9XuD=%v9j&UD;wYR2r zHeO3n_inC)i%#4fscnPv7ON|cbg%B7>Fo)+wOAp?s&1~$)E|zzJEIlbxVPNdF1G#} zCI}2u2$%y7KJ2y`7g+1}|m2a!cZ36@CeB zmEBJ6j`W)^hs;h934Vy+n!&}OEmYYJP|P@YI0e-~ZcY(>R_?djyzGOIK*DGK4T|?L z4iH;3s#Gp$^VaaEyf}I)k^f8p4RGEk+oBOJCp9yZ&lbmtprXc?)CPx7HbU&GG%&UJrbFQSKp4NcS zTEmAz zy9h3L9hEobq)!#AHX9k&|54Q3VwI_`11Ge`EtJ%r5=$F%0P!u6HR8~P>_*vtgXS9> zhSBF;cCGf*p2&ur%s(y%-A#7&veWe5SBy9FV?&~4t$L!E3%1h`PGEb?fBNi`$Xq4&#r-2f3hWg!lt-vf9(8onGydLi`uuF52-f*Ya{HpSXsd z@S0-kRrB1-|J~H*VevnP!)yBDd(%J4#20C8BXq5e8vHm_c-bo{Rf(r1i9_Wqs#Q;!toU9@$?jMmp@v=g8qoZ`S^)rj=6& z7x!9zJARW?xQ+_4X-t(wL?upgh)7~837&fL;`={f-fQ>jt;ampWYxi5xt2igB8a-j zzn;#=OXRAZ2yAO-cuT)M_1kfew(+i#qBRewi%{a@=*=7R0Tp+;x)A-rQGTgT6&bovPO5Y(NaA0$`a!?@1R@8vWK9uzF|hiIAMF|az(!=Hv@m1Z=PP; z%9SY{;ckf1PAqs^xc?Yy@>a}ucB0Zc(V1QBsV&&_D{Qu{rpI^-c)o@1sTmLRj^&u( z4+O0XC-#J~pFd4>B)u?`et35}7{F(L`d%1D6HGAbTzEdtny5N1q(Q4vy?OZ);Bto33rb3yBm`=}7BL&gME?=TMtJ^_Pm%s@=k0 z`0z@wn|p71!C=^QV#VyXCg|GuG5<^4_tgxB zYWG+t>Y%Fd+fnn**}<~ccU!pC)|l-g2J#2&#KTbjIt(`5zkf^kLAQv|*bPa*qW_40 zsero;!U?pQ3%XI<&ij^aUzI^57e;l2-ESvWm+PtjqD^ftP2L7i*ZowMwiSL%1ep70$nadSUD+#ITpg?4-pl|hw?cu^+&oD z+WaTaEVeSVx!0jhFpgfhB{xHy_uQ*UayWmlWR<*WjX$9P1nLMk&`@aA=ZQ3+Mpsug zcQ1c6g8FLyTVK(otk|%+sAO*T!sC`?6oAjC4o`gU2({&*}fc_F1Fv z&Ul{rM*45iN0LvebIHWMFxI+>6njkNK-I%fP+natIj{dP@5UQ4cK{<6V1|&5a$uK4 z;}6RA_C7a;8n*0>1+-SNkHGCWIkU-J3bYAWD)!0V_%q%D({6lM_C^jiAc6OyX0Ke* z8cj~CC7S>-WP!K2qcX3T1sV0L3xaOvZ)2_LldVzvNedJ|%K9~})5&VpiR_Q*uOVg} z#!$CTRqYA+v5AbpS)Y?MzR3N51nv8*tNOvRZc=TqUif~~ANh_StolyF69p^Bfd?6RK#*~C!Y_-v zo@8I;U5PVZZWAmqS9_{8?bSAIwI*V%KwOLG4RsF+!_L|+|KH-Uzv9GUUld$BC=B}+ zW{z#fAPV~wiNYS?*ovRLoLvj!NzVhj{|_P93a+eAw-V8ke<&?yJ#m)XB~0J+6l~y8 z!C7u&C)`J@^C|{=sYrN>v4qq+LD=xpML5dxnAabF9VIoN|L0g=|0!0Z(q~ny7d!Ez z;%9cQ@+r&+*gGDVZxVm~u(-8^*A0rl-iVAWhEVPTu0}L^GKMbG_&AfMbYZk=^rt0l zl)-)LZ{7te5mC?cXFTf6Rhrb)JB@#_e<%A|X!fRRAPUN_X zJ<7z66T8ZVE<p+&{<* z_HI$Z;2!E997ZHKp{8bsvf!c5Ncbovia~(ZAUEqkhuIEvf{`W89->OUMTehjX`p88 zEaJhg?nB^fkt1-u#%!}LyhY75gBU}5;?u=d=8czPNMmsL{7lYiy&T=CdfBo^5wx_K`n@gUtt?Pn7j`efQC{A4CWOwzMe( zqzmlM5PvZzA03@iN6NOdL+Pn|X(t`iKg})=Ca*a{82dHp zS=r$0gR82;!_vsHPMo0?J2!(cNI|?`dbaonhg&NQrsNMs3WA()YjaL%G!tlT#q)(e z3YYiCTc59{R$Ae%&n^8y)}x&u>tdBRD9Bo+2gg?*B)-~G@zw7XF8e}!^*h8@bHq67 z7(2rvC%#%@uLt6*qyG=_)zSh7Elzwjt6dK_rky`84H*rs50q;SD&zpMnvd=7^3*Y= zFkT$2KYm+$hERzR5s~H&XwuXg``2XMKY1Xu+WQvl(Fv_)HYKb&?Ukxh&IzkNbF+wq ztP5?^aVMg>nS0ZgGCFuWk<>aJyo$>eEy_aYzML z?{9^P_`<3kF6$6K3k7&Kruu31ebV6X^?~zLIQ2o%F@(2&2OjLB+DtTii{N`oCvNKi7tvjhI z=idq$b7Hq~7FmlsSxEg^xgWOxJ$QS$={Q+jx5NmhWG{iS!9%14CVCA&OziI zUN*!ys(PyTC!LUK4lw?Z<|>k232v{J%l9(SI)Iu4SPQI%Q(DkSfb|&yaBSQ_%g$&w zUfqvqn)!<`V@(38Py3$Qu_c(A5rx!N#xh*!>{9HXlkTLzS}sd?TWh?M(Y ziKfi4Qx|Csmr4Pc>jmY)OYESJ@+mSWxY(%3T z(1tmf_+lsQdM$s`K*UXQ7KhfJiCIL+OlrU9B1q_KaM2$O3e5f?5*TAYCyv0NuBnj4 zE&Ibd`SAa-_a=Z*R@VamovcF^CM?k)Aj2joTLiShK%0SyOf-?mB4|+)l0c#%Ns}2t z3mQmh31cj^wvV>jQfsZ*;x%{0J9K+u!oP^YWl-u3T^B74(FW><3nO0MPP)9NV|7=#*y=Jau-=)C=7ztmS>>UMu@&b)hV z74%wK^E@4nr<~oglk?bR|$D;u-7j_OAlWM4*i#zF4&voAZ%y7xY3yJ+nL zC1JBM+e=8cO2e)HG`3b#kH@*PA9i+T99A2}@uKe)RZ#i;ou8vYip^R0MbCr0X||6Y z7y1Ue_gi^#Aon>=J2$;_)1U2)RYWR=h$Ffd`|dw_>wwzAQZb1C6c?V`h066F zL`FbaM_2Ost>?BtTifq-_8>dW9^@-n*o*nVH=RAm$vU8x!;uiJ0J4y`bwC|Pk5F#- zNf{iW$G0F-(tOF~{^qMj=Q-M+U~V3V9!04^wDzZ6jmFONKrii295Sb% zqotLj75Ghe;E8+WDK1}YN1UAYmHQgrcXI+H5r&`OaM06uk-e-hz)qI=$8GZuY`E2i zxi*|&miwCu?My}VK@DbHhQZ1pt@S}2V5rhdeYkiaEcJ!;rWSIYiE>ra{nV}&8(HS{MPWltdbla@#3@Q!p` z`|6RJeyuYe#qX6)Qr(~ zYS$-A*vcNoP`TlcJJyq8U#(F3zCWUOtTL`V$kRhiGqUsO(F_&nxI?!pJCQ8HJDQ>P zMKnXnGF9(L_CN*^%}{r0ny3jD>s`rDBxDGy>{d^sFeoFMq242M(G0~srI$>}WhXN4 zLD}ljjEuvdq#Wue%oxs|#8wVv_9OK9n#W`_o!eX?3zX~EiEb!3hn3@&%l<^F_o~io zoej!s*I%lA`;-3!pE6oG)DxrZ8uc{RL$S=4IZ+Ct5>XJ9$cnm>I#3;3=`>Q6X2+r< z^gWu13sb$bdHL69i5ehUqOwnT7Vuz5a9mA)N}_sgVo*5HkCG^Ej@w$Iexnuf1R`pp zL|vCw6XoCZC+MF&olL0ytV!JC$H3tLDx&cP(JQuaHVH1TQ_KuG}NlwFOdaS;Q8>}>oSSph*kl2aydrfczL zYm7P%Pf>cNI$LGV{zUarg9yPK^hR<=BA94mGD?XC%Iq%=DIISyKrl3YF%5sgK$s#tQ1eD1`c=7^@>j8J&8okGdM0W}nyiUWh?5ZFD}$ zM5#rK)|!2cQ6Htb4OZ|cx)C~_W;i#hqu7G#Es8F%qdE%lI!_aly()Zy^NoNOjOeX7 zD$v2ombHuCssuXr>#UwqbClk-un?3QG@7H9b;z+tWb=YXDrfUjFW&wh%~4B<)ZKZh zMBRBIe_fq9(k_XxRz!2u3`cELrBsZwae2j*%q%L>gp{BmEtDn8L|qv@YNL*`w_}YK zOkmZP7@Bjk2B}?v9t~38TQ8mU_ETw)vVPAwhrMF`#x2qsCu=Q5ajwiG;ug&L~!=g1ugdKwH;UTNx zCh2ROnD`JvB8K^aNgtx>V-!c2`t5lhs^+q4i`VlY56AR=qFzq#5TTXl=n*)l_jfzx&&fit-P5?3e2QFOj(uj( zF%Jt)NVPK(Zg8w}Y<7ffLyAQF3HoLhL>R3QolwZ+abF5KKmV-MPKNu?T2mi&k(8{- z&31hd1P#ovXnN`+f6`&-EpG71#ZEf8*t=p7dWtIc<4&O!o7m>Q$)WvxV0Z?j_~81&r3N@CD%$> z?)tgRBCnybQMPF#*^qWT3?Wj*bL6#0S-t;Os;PPTycnYtKlEzzeAmP5Z75xvwe=~f zL+)(GU&Pcb#o}7t$V-S7{`c76B8gy|jA5vOZ?16#rv^&i(sdzn-ist~Q=)`|>j^9J zIhiu%Y|}Eym&z?BB-8`VBj!n5q5FyWJ&xWb$~O_Y$Zs0|CyYE8#C^{v&3#W(Gdcas zrS{y_%WtB!tPyNfshrfU_aP|sZ4+Z7c*jvPY_~sWqtX4E2HHgfp*xK-lkNPKvqo_U zD&#XOBXrC`-kP)zt3Zh*rF?V0R8g0B=j;#_Eljm-){v1>5^h9s(`J>Bij-d;ik`H0 zx6ej-K!`KJ;25qExJ8$BUYhl%8@jtuW_^sM9wq1Wy=h(=LXpsf+<=$<76`jGWa&pN zCN-?hhzpGm=XmFMQ3)PQpEFv(z&T?CB+gMe-$bb1dX@8w14Jg>PwGJ8;4F}y-ktu7 z%k{ye?wLk@zE|CFxriI6XFVokC9?KMC^1q)OnHIu*6l(s;U{O!;x1tG#uhR0Mul9k zd&4pF+HxH3JZdkmHhCIlvdjj>Q7u zpWGucH{BzLz#4Ii{%Uiaq>vykO#3IA#brjTb} zgTMy9jvw%6!>p}1I@TM+8sAtW0MHUjF}@qI>AYdXEN&G?>^6zr$_=xcqwF?{-NFsC z;6z3IeM#&-yI~f0HzRgi#O|C8vwj)VV@EvbAm1?Se(V^tB@-V1t|pz^@Yf{I`mQt2 za5U+XaPmq;p;Ql>K$EVQj%T53(?ejW^(M1^_)VD&bAGUocti2lt_OvLM^^us_UBmt zrOkM!@dwU< zuU&76-JBTc-sgE>hPXU!jlW3G4nz8Sz%{4wXmB{Yho9xtpYR3~_&Xrus3lzWsoeh zcIg!I2rl4lWcO@uaQ^H8!Fg23XFQL!S%EyMoo-6O!#Z2Y@NQjr324_tXEIJy$ZoOI zb%2oE<GV& zAfpj;=mRWv^&NK^+FsZjp8(&4t>`05gx*Hpf$u=moXcc-LP(REMNc{mX8ppG-aEk+ z*V;?WY0;yywJr%VXFo(v_w= zmEX*}uLvpx%#oE$C-xcCDq{8Pf;P?hHmi6aP2S>f8W{%fjHSy;EzsR$Bddx2)N6yB z?)2X-)=LAEtfx`;r0@}@V$ObLS*a)GqY>;4nN+h73KWR1g!~JP1KVv z?<5f|Gb0UosdVsXkovP{N1u*C2bW+hP4(KSGO{*WN>J86$#QYFvrdFg-qrKx1c{Qh zI1bFF!!Vgqj-pmNVo&TG$|-b?+)t6YOPblsy|N)8!Zy!I0raoaLr=;~sH|V3!*vFA zxYq!mCY-qe7YN6X{W{?oYcf^f*VFHOdCE5@VMZh-C2)*!Iz9zw-d&WHKj$ZlaFg`A z(}SS?@R7V3FLkH4FYLh=ix+%nIgZ&_$X+2UpGAu7EcjtF_DEpo3_H2}?3eRBe%fi| zH*RBAJ@y;0+rUg8(M4tEH$T?L51B{j+`(AUPEit=4Xb=c%Z6F{ow<~U%{z0KX3*=o z*OFi2o2}RQhx21)JD)R@-Py7Pl(DJ|9uwl-ze`il&Q+Vl#2&i~?OU}TA9`dPS|{da z@2`DDQb41cle$@dkons^4Qc(+pMp}CEjBejA>wV#O*s|@*wKA~rH?MLvtv`~p zdHu3L)B3L5#;1brruD~t52ke{`kTs*`?oMI6rc}hz7VBn)7h~3wb;a_u29;sy(%Y> z;bT_rkzj0Y(#PSajou1a*s8@h_6d}~G6n}fkBGaEFU#7>nMhL>Z;Dte!5mS$GwbF2 zNH(c$_~$6M9uR$$>koHz4$C>Z*Bx||q$0^TeIl@!(}`?FSGb^vXNy{VkHRbAt*W%( z6!|k&{tS~po`&~xLXElEUEvozx9#E#KYR$Su;J%A6FJ}Nj0wLG-un=7s-`7-#X#)okM(&;HD5B zcm%HuM!DfXJX8HZRQL>!iX`Y8W&VW(y zR$9Z8abyL1DV68pKc&ygZOnLYl-kC(0v|u9m2s~Pqjnr$b96^q@CbO|HbsPh> zPf06V$NqyVRvofOfq9kV59jVd+f(5_DJ-JQYy#Jf%Y=3&BH(MG4PJ&ab^-4$pgo#$ z+w}#ZO=0{##Mj~r?;YF9>oD{5WwV^duHY=i9PGs}Emq#f`!8ViV1tUwuL}OLa78 z@i~V$D1Cc(dZmPZDfA=jg>OhzrBb-g+kM%OhZ21C&*XFt5Y-BCpzc6;NqaciDuq%_ zN>@Zuvc+ba134Vl{}tvbxQj0|qb65qiBGO{3yl(1abLQ7Qhl4ngGRyM7F z);e@R?<-GV*|7fEGeT)xglNP6pL66)r8~HX*ky{g5{O(Z?lVDk7RHc;howh9r0~A! zFQL?M?vb&(LUGCObjBoi(dfG%_jz+{0| zI!}K{-k`H7CW#v_%R1v^xG_B$fh|bT$76B+@cNdf^=+O_-xUj_>?mu?*j8dxIi5xl z^%Jx{Si_OeCSgeGy9Bj@WdHHHG+)C16cDYn+Ti?n##oD)n;p)+WMZ*x7c$Qquk^F>4>9iS@ ztjd?|6w*mSs!MOhoUC19Ts!Ab`rukh?)lPSMjt7?=gTK_ZXq8Eb@}Zv4f!bdRYd0C)DNii3`)*df-(BkF@m7?X^%8&$r|b>Wxd&z4)M}-* zv?sd#gR*k6)*lRc^&o#n{~aga?e^Nk&psJ`JeycW&}X{HA&BYG-(qzgH5NsNAqJ&m zJQ-17akwS|Lr~ZY-RVOww(H|O^pQx&HY@dO@LWoU=ONB6k`i=lx%Va|npdf4zl~EW^6^)(DAMvDZU?yR}e9gGY9?unfdo@VC58v=)7$zGJO7(ZDj% zD%K{r{90e%^$VZPk32!RRah~y(z0GWMr86%!b}2ZGL_+bbf5&;tXAD}&FPJ-|78qj z1PdUaqQ~E`$ND1$mRn%8P#W3jkj05vWV6&1!3$QIS-x zpFhl$!2#fYtDC&CK9&2?Fg!vTtb{xdx`b;xcx84BTE6A-aD(P*hW><$=fR{&ER=*l z)A0U@P^vZct1?gzAm+}wh`CJ7PQCw^h`FHT0SyT0d%$}Sxl#a)`1t zPsxD_Q9F@G(lVf)$Lieb3Q8Y7A%b_$Sg&FLIyWkx#mF+&xlvgVJK}epvH9WObDJ`2 z+)cB&f+k~~1pKj%#g6ih=qR`Lj&j2%N6E!S;dV-tOYJC8sPc?;ksakYG0f>5;nk)C@nj{{s1dkJn@*;%zw0Yjry74?#moL$dNvy^hU&Z6B<>S0J zem2ltIc^-6KG&YcLe{&jUzhw!re}9LPXT)?UzeT*7Ul1NKqmHM<|rCiJ>} zo(K1K<^)O>h=}otrUh=={4C;G*>o;%rkBjey5)qv@wyf=PF&fP8E8m^g{yZ-FOWTJ z(`+d@gt&uI$TC-7d-ky%^m8Sg%a>+@$!jikwpU5S=zVSnjp9yv9oeCkf zz!7ZKS)27ftlmDtnTQAtYvM!Nso^4CAM--*wE(giPrvdI5 zb{rxFqs_jqNVc%$dUVJ~%2cw8W=vM&?Q&YOWTl*zT-lyGt7K?stZc^C1j+-6UbBQ2=`U z*IVS|M0%U74Q1qFQTXS1CLsNHaG*r=`LMO?s^!_URj!~ntSuxHp2qX_I1y1Q+Hn?U ztCF)1b8GLUoC>=w20x^3G>Do!AYb7oA36(R7Ne4la3HQb{Yr8oo00*9>Aaim6nnCc zdWIOyOLpEmu+AxyR+sV6^8hUE&8%D0T#i^SAFt|JQLN*U40EoN>745@tif5fbvQWj zFsZg{`uRsq`~{n6 z{TW5z{!L!E)z|%GPF5B*+}$D%{dURaVP@A!ySm%5 zcQ-F{t&0gC@rTRShL5qP2_?eI_lJw(j@?1qD08!N_+gai%jr?WEAS%ML4>=y(~EqJ zyTW17$By!xQr=FkN6XK{lTuf~hiabNo_CA~7mml&F1WZke@a>ag#hdv)|?Y)_Ic1k zw+1$RO91s6)-{hpBa_yO!UiLIndx}(UnvH2u- zxtop$xyD!{rvuWtvfuH1B@W2Ot(@6(VkA6q$>cW+o>-KzRDCla15pu1xL|e z{`qNn*==ihMB5ZPDG;eBUG}O+?6M25YxU`r~I1c<(MNl&?*1!G&m10$)Hme zotwzmqVOkh%*dvHkYM2OoWJzpoqS& z8c`8(#v;eiUpW!Bl3})dd2N0l{0dP5d$u~U7i5RzI z6xCqQON(e7tPCvW_P|nP4=nOnIKon&PeVav1_~-=?bNG}`~mD`SiY`sP2907Yr;=U4;fBXLxFS@=_9j0D}AKh?jui<#T!>e8XqM@ z?Q53NFF5|S`^CAZB9V(9j*v*EH2Z%>$YV4&2hAy1&=W--nk|gS%?05-G(R0Bw#9ny zSHdTG%IPzx6%mf9J*{kIdk4LwqbI3|1*3j313u>6+rNbQBvTmT=TbBPl z?OqgV_Z*+x0zGb}TrKOd+jV==j@zgtY4;pycg+%!#^!j8*5%eG4XlaO%4M3Ng_qea zTCyC?{;RiyB2WP744x4EIb zTkA(54V{2!O!=!=qA*2%o>7eylvC{<{V85n*K;Mp{P5x=S%rB6O}_SgQJWI>CvmTM zF2<>-9XsI zMH#4^kY&~v3{qs38>HO2+8|lh zWd^CS3JkK=@@ph4b!KH5oNa-!CL4UK;9i5v_+kw;_;$hF2AAo?`si{gO?ZdkT?Uu= z$a>A-ErK60c$?q{41Q4X7K0xV{Cw+fzR@NI%mHu!eIy#~KW@Sz6ZA-LP%I|cvfGRoed z#d;3NZg^!+Ump<2PJQpoY?tD#=fzj}VxCh-?KC!fj17`Dsedpw_ZpjJ*qlyRB-D0e zgCe%nRAY0C*yz;s*rI5mp7ioE@F;pA30h`?da=3H*eo(O6S3KBY<$Lsm-|w0G&a+W zO%^smV>8m&?7yKDdC+k=?ay z3RuePASgF1nAFCF$$6XOTwJI`@BulBCqr)%JT!3gAnOJRIEDl}A4jRlldOcyuV=l% zVkkfSq^yC4Z*k4tGIYQNyF!<7Tb!tt29C8R5;pXwtv%b}i<*ZHSh?YqZqLdM9~}3r z+0P;v<%y8UuF$XZ9(K0R`LTxs4P6urO>r}J+}G&|3}?f4qaxuZrP@X_GF?=3XZ;(0Dcqm_MeFIGk%>jiv{Ye?2 zsj?swHF!_SyzZUEMUB-CV7mDN;2~ zt$+Hq+AP5+h4787?r%ts+ANc#P<34Qp-yEborB5ne({6s-yxPn-RUo#i*-}{WRVll zuEf!lI$n%Tsb?85!GMVXokln{U2M9?-%VigujOe)9dJZb9kMpz-=dBVZN@W>h7UEo z4wv_F@&giFA%6%F;`k^zb;iE!kI>lP)^Ny#1->P;*W6) zu0ZeyF>ojz+C8E=Yz{W}`y zcaS*2j&DZ$c!Fq?hIecRa-l=F=6483Aj8n2hy&$^-?9EmpV$fseV=H$)8Cvb4b_f0 ztj)j2Ok#Q2dpsMzEfp0$o;T^E{N`#mZRz%GWYXXYxg1B|%Wj|iGM884J037hl zJs5~Rh|0a%LE`7P+eO)wP~I7uXViQd)9CZHdE)jcZo*fZ9cb;2MFRS`KGI(*sW-*D z$qE{^y3^-tGw!@}mVSY8eDw}-{4a^onxXqQJa;Yo#{CdsnTy1DMC{Fs1D(}URxAMUX7Id!5Cvqmo3s%yiu={c#E=F}N^ zQh(3s`jYPDY5baWt>*M39CT^U7B_LYLH9ZCi?5lYeH;mh4>)-7C%z()C>;szOO5;X zi2I+kd%IZ6ee=&4_o9gV)7rhoaX%ddWsgMsI1W<3W601jtaL#Nz9I7B7iMxgUTeHaGjzIp7JWx^!yK1ZVO-hopj^vyti5 z^3}XFMgP1-vhz%ENO$@i+XH)OdG!lF;nat4?oJ!N#D7yXR+%Jlmg zFBOCjifS^lq5m8XdGWxj5l^r0WS zZ8!;fHdQgu=_r#|HZ8yO1cK^?om(XrUxBlri_{Uqi|%)v*m)b?kGV0m;r-aqY2kS9 zddO^aI6ec|lsZ^UL#(BQvuC+9GA}}$GX3_9VMdTq-!zg+NsdPoqp~lO}o%nsCriXtD9Ni)9?3NfP4&aMXkHM-!PA+N?axyv6vOwb^la z)i`8o2NB-9@S(l(S_${3gm!`>g>^r_J#g%=dD$&>9?M5CC-KfXs7rq`K;K|jalR!;U>@sl4$VD?p15S+moLtg7g@xF#XkCKNaL>{)V z;M(qGzAZsdmdy4=IWb3b4xT{5J4VZSyFvqyJJULHzvVmX_n^f(mHV6fwHE7ZhtL4~ zt!H1BM)Wj3$!3y7e#z1NMd7%1yeG=(^?g`dU8s#=Eawn=Ijh1JJ&wnR@j2`Ap^_71 zI6R))c6XCo%Zi!T&2IUbOeDggHbJ2RD-NGpD(PNvL)3kQEujO2h)?TP6gO!P zUlk7so@D92nTkPiIp=}nip=Fq{lLbI!y>&AFEztqp`2%MABd@5t1|U8ULsKtjw!Zu zoLB0(K-RzElr|C7=DdjlNG0Zm_b^`*PFh5cw|G})(#Nq7j=Y|}%yV-*c1WV+Llrc> zSrJF#e4iyH+Lww_HFflg*p!X;Q_nJS?5l+ZF@wdTh1TyW!(sf@_vEmL8-JAXTk~3#(QX0!@d|1xEgJ%`VAjo zS}4WK=OSK6QWDlhk<-n`ec5~K&gyvm_1v67{>j(FspQ;b&a?4C{CE77`j9T~*(8iiT`1vTX^*EW)^sK{%GIzZ zMjAMTaL8#s8ccLy>8Y9Nd1VueV~iW#KOrUDed-c!U>*LqN?1lC2~ig?@}8=M=TMNo zCCru*4#Gi~t44h6lp2se${}e_Lrv>lq7@azHKgUkDQ^Fyz1`^r#KSC<9LplypMBz{ot>=M+eZsUi!q)1m!`ms z13t7zY>cHsG!-HJwn=sxjI+xdK8z1tY~@}n6NreOSXY2HWpwO(AK&R;)zNi6AtOwH zF_`TqS;*CEJ>Gd8V_N1ulte(Cff+A#-o?C-MGPHl3GdMHxAh7(u2s(rIl*LK{d$F- zlH=L*{3RULD%pG2RMQ^#hR24x*NKS$!becWc|ON7>C+KlY!u0l{p z_yFub-Hsr>?7g%m-X8r;^(KC6nNCs)8>PSLC04Y3^f$eR)v5G1ZG(r`M}HHKJ|fvQ zlmHV_>u-7o4;0sWqQ~P#?jy)*jKOG`ta_Y9vn?~{luycL zYo$uFwnT#KTlL5>Y~DoCvgZtt60MgDLp+S66$+0Z+*2ZCEZ>c2q!LxZdlyq8u{9d0 zCSmnCtn^M~BV@qZ14Zcd=)j;xg6bZZKUqZ~LPk_S`9G1Lxa9Adp$9;t-87evZt zwu{R@jb5spEw)~&oGqf4>TFmS4gZKr@wxJUR)m( zX{As=b~1sc-0duW(Tn@xDk-t%Al-k7xb?3W_oYPW7p()fUfgevjMj@gDxZ39PG4M! zpX|NCiBWoSYw(!&$$D`M@DkCByZ2M-#r5}O`(qaR{bif=V8=MJEyWElhPT2RdLO#+ zdg;Z@MKv!vIkcX<*BHfm^x`@y^h7W2uW;$37x#gi-7O=c_2Sw}v|6|sbfM>g#sTfP z`dTeCXzA74aTH$29OetRu`Kp9mXI?MKfl>+;7l9-)wcgH8{TfiMzSDc>fjH~@A${oe<9<3;M-zO@{{eg+DUq!i*kXljh0wh7A z6y>^;b>mZrUW#((|6fy-dprG#`ut}q%Ke-)w{`JzdT`J)JyRt5JH7v)qFhZrol_-N z9ZQd*-1)~lMNuvwq9_+UE;l@%2e444`~L?;xi4>Fyga3%Tx1+m-@k?puvA{OqTJG9 z(7}I8QSQS;+nqi_$J3vp+*`H#M;Vfi?S7J?+^ZlGI0gR-MY%VzRtbc2x0_49tn3P- zR7)km28V)9p(xic%N$wqwah}v_oq^n`vfB`a|i2)PoXGxGt6r7Y%9t=>POn~)QWPi zFutW4Ppv5TtH&Z0O`&(lV2l)doAusjbW)C@Ts^n>Ww{;El>1>iLTg*bXid52u{dSI z2`2d(jwhm?n|k&kVN7Y&xpL`-2d9My{2jFz`l`;oN_((X=U(bWgX&y8aI#|1 zN~lBwqUgIlmFnCK6YvvM=N^T@Kn(7VHw_Zk@tQ$g9WMz&r6+bAGFCAiZ6H!>{i@D2 zr4i@F9pA;H)p})Q<#>bNR-OCBqtSPmSoDkVtY{+6Vm+`uKrO>(;?2!XI;?DI0_-1n z`U4-bC!H-yz~vaDFkQ>iW{G!r`VX-$hlmG2{#yvziT(kO^>?%Ae0u|Na1To^CPgdF zmAzb+;cIGktep3Lial2fvCAgsHRML^$4_8th|b*WvgfQKt-7jSI&*8V>Tk~_muALM z-l#p-VEvYfqca!IaOdgP`baX?t)QeFFgud1QdewAMp!o(duw5j*JPAVNxk*6?=oiu z-SIoisFv_=_2X(*btC>!iz7owDz6&4_stt8(t4dkFvOw0!iz3wX(=3Gb5jM*4jg8rq~Jy#WoD+wpDpumu_w4 zY{WSy3CyJ%?e0Xu=#m_{v|@dKhPkogYxv?&<=G^xIxa|N>Gx()*>&ATa*Q-s1nYTf z1$Mpgws(I8GQ5CT&5aRj8fg9c<0`D=ic92XiHv6TlB0$_MOVw-o0u|*DRj2=F~pGT0*8E=L^;J7;cdpYw-x6ZvhG9JDDId&c&5P5!hG%8Sxx|fINU?B&~ zDpN?!qBn3BAfB?@jaK26v&v%v;qw~Hh2g~$e7EfjWS@5S0bGB* zb=kf8ETG1kvdpOiJv>C{zDJ$_`p+e{IF1ZomZZlv2i(>r^*Yiol9HqXcWNT_uudC~ zhP+yI+2g z*FkB3V>_6Jx&9FvCb#nD&YD^7Y5X2OKvO{Ff{CdeQHvIWt9ITyE3uk%LL>o>y_`Ga zO*n#^Z3dYP3x2l9H#Pa+`WA;%Sii-_Q-i*m9Eq3NfW;mOPW+ zG_H;6YjYDVP5mB2xqlWn5`!VByi2IO$Mj&`oRYwcQ-g0J5L8DFmx%jR+__dkr$l>B zg7w!tA&t z;pQKwr6{!Pcg#a)m3@Nz`iyRQ8*{!3DVXzEf6Fkz5+qoNU5vbni)s}zELOqB5ty^GFnj>4Q0W^$DzvG^~P9`tf zKdkCuUp{U97x8nx#*3DD*)Ipe&*gdw_F=u$oZE4EZkx%;jl(-%vhs+zAuB$VY>m>% z=`#eLv=V*&Pg@)`k=5@d$=FDLY@2boJN+k^I+t93B^5>Fc)EaPoD|aNyTsy;pOt1} zT~>qFV@CqRk(GIw8?S)|6fBRo9#|yhl7y`Np!(E10iUvjfKjNI=R__qxjk+D{yiu1 zW6(RR1p)djKOYtJ%_B#ym9auUrIlwtrN!aa{<&NHt}Hlf%U$l67T<p5K8&ux8`wE?rra&>0*vEQie zV>&bN<#ONDndZ2Y32d_JQQ{Ly)Ov1scmN`r02AP&lx4yN| z8Ir9TcIQ|m2I!!KWyurC+&fruzF2lZK?q%{tjDh2StL1!1Ws!cNde=s@ zeFSujvUrvks(0!e)mbb|j_!heVx6O1$4DyeyivW&exrI9Z&Yu=QZMgb(Qi~QI@KH1 zW2I9@-l$GDrXuH!>gEnL77k5s?Cgo5^dq(!*?7a(vB!80&NAp?{vkR%Nv^>O^Q^m~~5Ggcjzri`Q#-X@0GTA@%;N;q4=o zyk{cfqQ??MT=c+7IYZHlw{Yr#%V|DQbdX{_q+2reVPn$`H3}7UjyI=r_KE9a5dwfN z=f)XVx|Uo)mJMa zY_qnp4Pn|q?cKi443l!tq+so$5=4onsa($dNER)vl5WEr7)ntx!5F?(|>BYcCUbcaI-I)pd_Q zQ~zDY-<&0+=ne43B}$E~wJjA5Hy^$z)?Ih_vM2VG^XFK&ukgQ}{)B0x86pVH{6n0w`Kj|+w zLIc*(FZSo8$LyE-bz)J#y`g@cbf_OhIrS-?4!SAIJyP<9N9&1}{h3))7Wd~TP*Oml z*b}5L4AQ9Kp-)hhh|Z&cyb#}-NlXs0{&)9UEu+mHq2w0Lf74f8LW zE~GUY)}sk!2AkBHhV?ViU=%1pwoL0o;llhW`nZ|vp4KJ|aLK|LV!*w>?X2S<;_@oX0%ZA5om~x(pcZ3Zm+c3w5i*2~h zhPT-8b{pPn!xkGpZNnFB_?iv7Y#4LCNiWTYXWDS04YO=`i4Cu?VWkc0ZFsW{zhlD( zZ1|WBpSIzPHhjZ|37;|Pdu@2W4fAcd(uUXB@Mas{ZNvL*xW|TvZTN}}-?gFJZnqUS z)Q!uZ5A61S&4vkfJ2>vAf;HnsxtLNTiVbW(WMJVB4eT54cPtOZf8kmEyZ7}k=8Bwa zRtS`7uz;@%5w)(NroOrsxpaJ67aLfjN>m+wmZ-(*LN!HYfy>V^pR3MS`6|~~YWqu6 zo|>l?D8IH1sDQdat>)ha#3M18U#T+xCMz#_F#o*TPHe8X|4IG|)HH2BP1{N6LhR({ zv!URARYp$BNM{9qYsE_cE>YF$x_;~z;5&$$<9iW)YOv`WgHMIjD!w}0&d@=>Zw z-rHp{zRRL~7f{DlQDGP1-rFqsaMD#!nWU$biC@eTpOi!5k$g*dX3oF`t5;u87a^!p zev`cg{Jer(&ktXOE0|VVI?WggAse3|9px`8DO(o)1PaVz4K z`1q;_f4w3uiAP;nwn7m`Eh-L%YD;n8cVTUXjZrVSH~JAKB?^UnWF)~pM%XV3ZU=W={==lOFloFB-$=;Hi> zOBNJfx^U5ASewf(zvA=Due|E&qT&@LrDZEul~-JIZRP5!>Y6Xq)&)b?tyz2h4PRWh z{>Gcsyz1KAwH3j_+Uk-rY2DS(QVDHe3l)@cQKXcfC3P)5s0zxdz(*M)hV=Pb?awR4 zV81RaSfQVM`pzQ$Mf-7Fg`V>0WAjhpD&xumXwRp8^%uiJ#=lH8gMSj|6youbD>IJB zc;e-&=C6z(lFtI2Vm_FZs#M#p)Hz?R{gzXrI$++UhrHa4TlSfHa+-mqI zF`5t+xKz;|UTQ{4X<}WCt&Egj?ekjlZ}J|bS4-)o73+wxQv0b^E3uRKq)nZY$@tV$ zPg8U~ttM?5Q)SHYs)@`9C75c}*_c)mUlnF4v*bx^gc77ar9>y!RF%%7P>`7eqz}?Pw%+Y7X*l&hk9ZbimiCc0FfOL1rOqUuQ~xRS;grWIM`olP zPM)0l>#qc*k$N|^q3rsY&N{^DZ_@h3x|f)8NpI^fF0ZCRp<^dc5~s=6boH4?d`^Bo zU3;z}t^QN&zfGO=&i@C}lb$7g_8&>_yhwUA(5Rt->xiqCIAzsQNv)mSlbpWUQ)5p4 zXGY@t^!0T$IhVE4TG9>Dhoy!blTf77ian`0?Q^c06-lqZ_O7N?rM=8ZDE+%yj}|g& zmN7=ks8yz8aOlW^rab5Np(n|e#3L5@7SF9GNan>)6LW9%{XTvCvND&oy%Td?pSXM4GgI}9 z_bX|)YF&3S%E(+n8TAY*>EnS*Hk+dk0vA8eZoZSxS@+?8tFGi-CcZ60QuGi~!3wz(z6 z_(#-V{|arhTo%wj+sv&X{o9#r{3D{Se_ct&?6uAHwt2K|-s(2)Yi)ClZFUyWg|_(y z+db1Z%guWEO|;E$iuKQJn+tT`m-T>*2`hCQU51^slNt5qsY(3v(sQM!N{=)v%6u@{ zsR@;sTqN0RN=aa<{>S?`w6(;(O8b;Ghs;JM28r)lO#y58`&pf@zNzFA&a7KU6MGVS z+vwOXAU^Y(NDMM!RMH3KH?b;ISqTaYIDzvZ4(4JLR6}$ z=vJS}%v@Rs|JCc&B<3zEFXE@v+M2ce-1y)n0Jj_9=ARh<{i6oFC;gkqIB5Qz>~^wE zfR_2Be~s$of4%*0P(A-#>eKy;j$xzf`4iHMr`gH%C@F!FB-)r-?dVr)flz&c&V(P)uY(2n#yTryvU;m%3v^~Dk zR1a=27o%&>uYSYEhQ>`_`ts(cE#aG+Z~4mBuipB#+rIvd|Je4;Z+-jrJHGSX?ccle z`*;1|?jPRsqaXj|r$77oFLwOrz4zV!%Lg9Z`Ow3^dgRf^e%;c#YxkbV_wH+Z;>rC7 zo_hM3gNF|P=Go_-f8oe)Uwr9zzyGh7+yC&&tFQg>Pk**LUhjP4&9~m}dgm{HefPbi z$Bw`M!G|BAO69-JpgA=I=2J0f{?+CGSEv8quK&NqAUn0YW{~}>%OB5qq8#u#zmb?l z4$1i;-`30Aj5&HOaZ4XFw^4h!Z|!5|PI52zTl<*5*2gT*Cq}0yPb@~8MRGyb)Gd5+ zk~_c5;WaL;s4A^qvnW^`tbiYi{gS$}+661FDJu!)RjsV9U9BCK5=MTw=NH!neI>z) z>&gnNYeF>g&H$zoDK5 zUF?_YzqGM_XnZ^?x=oulQSq6X^@a7Dm0L}mkX5*}p7k1wy28TE1ocj=-&{{9#j>uv zyxi?hTw7DKrl#hk^_-l{T*4>kH{Rnk|y_ygvnz5th zY{*B+0@U#`AXjg7D0ueOE4(EHk;e6|)S1yPVqekIyxuQ_%4)B#s9NRt>FZus77Ru? ztJTHVim@(OO6e=AD0+o1mJ`0my*d=^&x4)69)Hy3C-_@JyEuFjhqqip%LsQuconi6 zl^uleJA{L5IM2>bbpowdiR}ctB?ULXlIp5pZFQx$ytt~gvaHtIx12rgx2h~i^Hl`B zCDo;6-s+X!8i!XeF-;}iwS3NR8uU{~j;28el8b4$$*$h{<*u!vgBi*&El<3wSCb1b ztuIwey-t`4y-X0SuDonTF_x7TRb|jvrIUSiQao$QD}rTpHN_=m-r|)(N;F|wsIGR} zii)afWmVTra02(xlTN*dE+sWQR@7EktuCtyI{tgoDXU#wQ3XAbo6_nK#5?5+q3U3n zm!Tqxyib+s*(dd_tKkd?Kkl;!MIT?(P1=+LBa zvj5Q&sM^ZP>NOIvgziHQrqWId*Skggq|#41#kH$K(%^O8iDlO@QdO+g#-IA`H&&TVADp z&gYm1oe`(bn0{JKdeP8K2_x}+b2JM6_|CDzxP~REVZl^2j5LNVPFBNaVy;bZ88#*L zyit2RYItyKSM%z?z`N{UlSgu$i8mF!Z$hd?IDdIuyg=vxz(_R=?9&>ZJVsrfAaaAyz$;d9Pter z5wAwf&1f+tQOfbZY>HB+F%>!GbtT29r0EHn!=?^UBTM(Fku!Izk*>$oz?PJnWINC8 z%-tr>3F5bAkS9)gW+p1nbW^WVN3AoI+RyillMhdV@+`*BOd$SDImmyI{Ff$Djs!Jq zdV0H)uMf^sYWVp|U1f*Y{;9`d)Z;Mfk@{^Pa&SUpIVV_YdhwpmZe+yxY>8js! z($7dz8MIFZ?UO-WXDl3gaBxdn&7h$PYG|pOx=xh3)^s8HA2u6S6JI3%2{9^RVazAi zzvN>Iw*L= zA$|ii-3(Ak3lk#w-t1Fq!d$eEkiI|{`gu}ZgOtmgqP(Sply`BO^3EKnytN~`&S=j# zcv{QQ8rO&vHKNp`Ml2qvMo_mS7G`vv)^692v>SBhfzCV&d)oKz07{+m;IVzAv!Yyy zF)GpZZQVbl{`8lYpm^F+rO~!&(^Fbp17g&G>G7Gaq13n3IrK0TH>vkw(*~$BgTGW~ zmfo+!33(MrQ{j3JRjc;vq zKBq%VL*f_{64j9D9^HOIKku$Y9~pu$PS_dX*YHKc4vtfUXQrybwgv^NlzQh{rM}iX51GTJB&*@2m#X253)S$M3)FDeMas0i8J}{C*?b3_98Fpi>V&fg#_)7i0G! z*YG4YJeWouFiulpt$Tr$3LDB(KY%@{vGoxT`Io1Q*> zNDa+t`oV|+YQ&wCt9{TxPfKb|N>OrV60|)?(q>E=&b%~y`srP!P7N(c|7l?-^cTJ( zPG0pG#@I!EGU$&P)9pSggc(HN|hXf9_%_YeJ^Sq!Tjs=J&E(e!|)V-qtuU_Iz5v);MU~!0S6OX;%nlH zVhdw3rA~Y5qGx^|c7C!NKl2N*<6SFb`=crEaPs>JbUUnFsf+m>x;@pL*omKMf9RPL z4K0^K%UASE%WaHjpOkL%o2*^~dQj1awUD5|Jvwr9r+@5yOP*xIS zvCNY;o!8PAMy9Bd!DQA@NopkhVI=)wF$qf<}SUO-!x< zE;V2UZ7O|ZBkPdKG3pFglUXa$4~=<%OVhF&e<=wng}#-t(5w$#qf*qU;2<@sG>twz zP>t%D3ryb0Pt^F)E^DAua3X`sIy$}s>lpfX#8zuk*~`{f6tsI)Mb9p&k)?Dj@WYWVtX2Ob5Bf< zALG;f{kOclsh5At%bR-mzbY?3$`vgZ+U$b=I?wQl^4VE0Rg>Ztl?kJ;usB%0RNaUv znrjOe&v0_pJ!+nC0n2OwQ<$>r#cx2fy`uQM^uqY1#TCH|t7{j*aHuS^!w=}sHg_#b z=6rQ^6|1W3co$dqB&kYW!~*-+<^_}HpCtcMpn34n>>x^=5rb_(b!n)w>_S+YmlUt2 zVCsjlmkLiZDsMeeSHu>CDuWesuMd_ju3lPES~jn|xK>>w@xaKg4TfqY*iv<~*er&R zVX8-c-L8d5Og^QqHK9sPRZxC03y(Wjn7gdVrJfcABkmta86=;JudgXn+2T5{x~i_a zvTRt3I#oL$$TARQwgS@F?JbP^yJ$US)M%nVs0hxeG7J&7U!SYH4L9^R1F> zE`(Wfovw&Qun3o`T-TytE&uZfeS!7?0l?0aI$9LSU+OE&)4^Ye36vGrTv%Ji4&pZ& z_X#sb{YB+g!PJ@ugSMiixN;F$5k|ktj?FJCzOL*f`x;XpwY0vJkW^lYTT)f738gf5 zZ3%+mgr@?CRH^%BhtFH~!TnHmw5+KvUGRhE_dBrtIgO^dw!LqeM^%f-(&Rb!1g_KO~7pe9r^V4yQ zZ6;n1jqTMv$Fzq-c%3nMRa8oGWyKABtHD=Qx~Qh2YF>4S9N*lh*ZI_V$(6}Gec&Zs z+tfo8tt$0nsmIG;Rq5J(PWv%^kX@RNu~7W0D#3m8nEyU2Ar|Bo2%bXCT}IAIbx+|W zMP1>#u(D2~EUrX${qtfXNn^TPOuFgor9GmkcYjp57njS~K_2zJO#B=2Q2COoREe*& zlqRZE>P)dQ{n5}VG%z!MDFmd+QmvCTjEkutb(j{YEDQFnMUKQ4RhA*`KP@(YUO{mU zR18%S+wRzfp~EQ>2juC>dxFRJ!zs-gPo#n+$o7XxPo-* zBt^m0SM2dcES4%?Y<_idX@13uS}sIT>K8Q)EG#n>zg@>|j~ul4y-{W=pPIRnRtnXX zOJs9HD_25F3SK$AzoxjhEK-+cQh!l(a#?Y0g|K6ns$%0(x?rV5q12_4Z@U;WRw(s5 zL%GmDx0Fo_+hZBo{rB(xMKzG~1tTZuWXUo2lF~?XTu%+z)}vxPvVuKLhM9t8n&eV#C|lN*mJC+{V|DZ zGsh>|%tWQbEVS(x*-+*SiT`pwNt@-Y4!0WUikPN#D6`X$S3(h9!5(5f5$d|4=DM)i%-(~A)okrkWb3=G@rP?z$fk*REpG(d{Qs}++W%$ ze(Wd;M?a@SmT~DM`SdrB6u$-hY1H{sk%gA18^zT(kai z#l-31<1Pm1qK(&Y9Bo(S-7g#Ddp|I6!*|>D$y3ga^%~xT|JLun__wmhm z{F@{H)&9Ox=iX>SqE@MUUX-t8iKadl+py4v`8EvL&~L*W8)n%s(}ojmm|;V=4L?dS z@pswq4I5fEe9eaKHvF9pkJ#{88y>Xb0UNg2aE}dJY`D{g_t@}u8{TF^C;pplbE6H{ z*|5fj&7|&}~Cy!{c#wer;&k@Q4jtY`DXQ+imE? zztuL^+pxxl%WN31VWtf;Y?xxhI2(4wntZg`aEA?h)p2xv{}1Dh7n$@=745&;UFzYJ z>Y{65|M{;OYTFMpu>K4Kx7y_`EHGw<4ZX$ED?;q$S1*gdf8wui+4`FMCf(P) zuleuw|M!OfXUE?+KYh)8)Bm^m|7H#Rt$RZ;eSWd_&nc~Yi0QA#2OH@0_gcFj``Nq*|$c-i9q z&;8gn)beFcbtyzg)Svjg;1+NJ8{W3bE>#Hp>JaV%V1FBM{ZOTH!0UlOjTQjb;Boj<;fo^=M}QyjrGbA0 zoG}@G0r*VdOMIE&zXQI{S0sLb=RgGI;FE#3@<~~413t_rdDsK=WGb}{`!wLid{WQ( zK=co4KlQ-3`3~@MU>9)UxhDQJ;4^%>Zh>E#VcKOgu$^xY@d#|5i5%crF0~bS0Sp@p z``N%9e7dfHOU_s7IQI3x%~{B*j(2Hz=PYv0z;`a7EZDyb9Gq?PFci3#PvUO_ z&Yg{a{P=<0d_r$(4m_66k{*l@x1|_16;!=ajpdp zE>LPR?Kl+ZUPj))1zyFc%L4p>PwM0&;AR+QQnv!vex5KBhzB@*Ic)?!6S#*jA6($9 ztGN>aJ{x#OG5j=ebmb{OpTr+rq12ltrd_&#^Gi+M0>DT4w&VW*@Etz!|1R)HWylU< z|5IS*N|QH%i}|En0>8XUsgLlp891Qa*rx#hz?X@i*MK7{jJ+3lzKs_Gzh>jN0r&CE z#(x{ob&W|Y4tSr9-w&KuiHsP2)&cM2)9nSEQVs24p9!qvll-p({(?{Z>;P_rv3M7F zBQU0pa)HMIzii{1fvG`bp9b8B30pO6$O1*~tP~fyC+7Ubx_#mH@ zaVPKupVXn+!u zFR=W_X8aWRnV*se{AU5D{){>ytxVv!pPP9^;Br1`7lF6gxWM_pV2r~5LExYr)Rp8D z_{f9sttYuOyk#fn;NV+1cuIlCz@_+;RZ`Q8O@5xAEzgfGnLbNA0vp6`eu8JPGUc>^B-y#FY9 z0~fgQ7;OXI2)vI^%5^_*-uvVk`$FJWKCu`0gpD5nj{ku968nk3JNRTA*baP&F9UmY zpsEvm5?1cz$UUQXp(}xMuSal!ayRB4=3jww??vnd-e%)+_eJiZh`m6$Un2em%KZ?r z7bvp$f(w*;4}uGnI}U;il=}#R3#_woxsxFG3B+EY$mk0$@MRkpIen3}7khys+by`j zxi&6PWRfLs0!7|f{0Nl0E`kdbnOwmIzGLI+|0BORU5+>v3(TbJ4g{_RrUADCGl2I1 z0s3eBC+hu53-&SC(tYYBJS1E%?rxTc@jx#y0fB%gUN{y0S-|si{>?nxHz=Wl)?H3isRyn(Z4>|?Z?GU!gbWiVEg5` z&xAU2}-Y=h^Q?;jn33gwf~KF;gyc>9iaD-INcggElY^;rJwrepw88P9+HTc;+24iuxt z0#rx)FD>&AsHmXN%ib+s<7q#j>+(L}z76RuWdOfb*DpTsUhba{l$94}?&3N4BfkzW zDl6BwSs(oUdPSM&+nWDu;&jDr*4N+aKUQ&Dx36(y<7{ufBkj_ss@!8&#)&$aL78~S z(<#@%u?w;mjjNQr&w9>qY^SE2f$Nf~T#)M^*GF!Q>>-qMH)IR)_2j%umoE5jGQLzh zY?ylV(ML6IM#dK}Uaa!+^3;a`Xv^_8fxCq#|;R@Cg-v(=MNKB=;@vea|WJ*So~U8+{B zSfN(0Uaeku;RQXug@uJ`^XARkmhITFLmhd&P|f*X)aFuAZ@lq_di(9S)w}P$s}3DH zq>dgvs*WE&u0H+rQ+4jtF?HkzQJ;VQxt5Eszy4aCKX*ob`Ln3f(o*%)Pd{l{AzN?q z#lBNCp5=kixGt`EhwT<9d!tgwsobst#b1q*p=!FMs3nr6Hp_Z-R1O(?s_p>1C*b`6 z9|-tCh1f#_{J2v10@6f11^5+!F97^5z#j(uNhkQWfDewwdLS9T?_#AE7Q&YT{GL** z#Q=W{@Sg$xOTd2(_;Y~2=mhWE7V}#OWPKET4y>OG7s6-6TCDVSDB-vICp0T26A3TH?xw=3!6uVnO4C6A^kc`i%Imi0=G9=Zar8sU2> z&G1Z8OZZGaP}^>*7?NL#Z#qkp2bK6ID;fH@l9UxnvNkALzfZ}b5+`^I;JX2S5a7oE zej4Bx0)7qPHv)bq;NJo~`po(S@TUM@S_yw2z6LT7EySaRC(yzRXkjl}_#7>iHWBrM zzo_#=MO{o0^;4FppVy1JbVw@U?*e=Wzy|^Te!!0f{1m{?2mI0|qSpG0+B8(uo)l5V zS)x8!FY22^SKwU%PY&D(_`3k#67X#Se;?rcG!Zq@UsTFaQ43PgR+gxu^`ed+x&mL- zG*~N=^Tp86z9D@>!t|v@`4+>!ep^?_Rnl^1-abM%u+S39gBP!ds-g!sUrUL_Y13sF{mbef6!w4S|iTign zy=R~Uw?XZR0^qGdAyI=PBf}%DE!aSF_h!xhbnC6R-aUvo@;*QIABA45Evi(ok`25c z8Xgr985tEGc; zNJWYW?b0{Yx2{XW!Hyf`RYVxfmE*p(hp$_Mn_XO*3;>2iF)A_$f^_A6NcXOOog36~ zy*&aqL;FIOyL1T+y>j1b?He+rSEKs%x}k?qD_f5UyVAcklyWzu*KIe|59a;QAc$+E z0SEuc9|&0RElnB?v4%zk^@6Nb-o|~C2Dmlp5fuVHMmXIj|HE_*T<_jFv=>yx`L=E! z;~N5g|RRLPI~Qrl=~7!@kO{jIzd=K_}o`>_X10$bZD2%Rn2| zv()6vWvZZR{@k_w6ywj`fB*g3ADI=~ztZ=3aatcWKR#S7Nexw-=7g)lWwTWGeefw@ z=LUQzY6N@-`V7-hKKtymYWeczYURq6Dmyz{ty!~1`wAO3Zq)wZtFOMQ=qtRx>qWI3 z{zf5ug}r6qviVWL~Bn#d;QFFVyx*{4$Eh{}>r)OtCE z`TNQ|qkJ(TM!N$ZlkMXB0N)Mp!GMnh{3yVu0R9QUuLS%Cz`qUnlFE7JzkUkX_g_Ed z|D>PNLRF*|Em}Z{@a}nY{%zsj#@E-kJ&!CcTDZ4p+1|sgQKNg|4>$MpZqvrAjc*gT z#_evyd5e~!BDo1j9Y+itt}?v`!5eQ#;#eXrw!#p2!G z*VXk_Z@{#>z0qwg+?%!cy~WkFVS{@0Zf)1Naf`Y)w`hOQEv^lD!Pm{>&iXF(JyF-! z_nrpz*#4i~Z)%FOTUs@2(7*@lfR=6Us&hB)0^}Z?qko^f@9=8nw zLpB`8;eRBNKWrDPn_4CE*A1ECZ&|f!)n730Y(IDI+`qs3?z^ur_k3~o?AcQ&OU|D^ ze-7)?lb?L@$@;h6dh2OCFXYp^cW+2OM_<2k!gjE_(&P4f_{Xv4#C&_(wryLXB(Jzl zU7O@SYSgHpZb+zn7xobsVI3_+MMd(}S6>O{Q^CFqH>jkfq(rd(7TyO9f;IT3r%#{$ z8nk|X`0(N9ckkYv(YJ5k&S;0@bT@J{Vk?rn)5@Y4A2-@jk5M=HQiwr$&{@x~sK{QKYkmV*Zm3icFq zJ;K08;PsP$S+#!s`k7m|Zk-9YFMRIYxkHkZlfxD+To?_%nz*$?_G#9vS!e8RJlLsI zC-TlcEG*2M?c(@%;s4{0KMH)AtX8dBbp;O>o;h(xanLGde_urR6?k=It)4%-ki~R7z4?n*D{`;?d`Q?`-=<8>~ zfX;tH-?{>ag@paR_`{zP^i`ROzscmQ$s7}ti9dP8(j4=g+0em~_?Qfw#bk`Rm~5_@ z@<#A~{q@%uU>rRm6Z1&Ro;`as4U`Y;Hwni8`%GG1h$D3ZI;_hhM~(~XIAUht-Hd9UkH0C7!OSY zZ4SqPd?h^`3*ydWj{j$$eWo*y$#e3X_)-=~3-KoZDWf^xiiF|!rh^ZQ^uiW=cf<@j z?*k2cMcRXgwy%kJzACct6T}9}%U9F}dDPMP!`6$*#NT8y7N)!rJ)s^@UjFi#NdIGa zC**zbpEO_$y1gUP2{a^rh`0dnURa#s8u}?}_UeLL7XHUq&Jko)2(DWEO zSdy2NBl5o{I>>kOoMS|}q5RYCV$G%z8Up+Weq2FAU%ZFe2e|eG4cJeXWY9(Yo+R@si=ENViI179HT`>*|sb{n;HPJzNcODZX zS1ieY-sikPSlSuqG~y(5lr(%Q5&;^*K||qL=NeZ%{vLOhU4@wDKmYu*V4kUxNrTCT zj+(~A(Be$TrAwFO&?_tDg&C2uEG0;uO$yeCJ&0eNAO5At&=Vq2q~RlnK0{`D8-1ot zfu0jB|=p;DhogF~M?(k8*Cwt-D*2^u`M zRoGTEGrJlM8h=ku&r17`IiB61E5`Wss&9g(K;IUqk2O3@g4cX}x_@|u{dH5U9a9ZRq zpn>{Kn-mF~Wb~Od7=0!UMxSYuytixo@gz}|)SP<8`2~JfWhMG|(_5P%f07kq@EHq`?Xr5Mz_SIO#KO67@L*xIolZ9_gT6WJ#GJ9rSrvk``X$Tx?`x*LuKx`P|0{COrDK_Ly!K8Dt3*d(T!@Q1zE@(+9#5_jUok~k4>>I3;~ z#)LNB**~T~L>ejoug~uzuYrbbpkdQY&;VVaK9dIOGi}mR*j3W7m^4i2D^H9E4RK*I z>ydCx1M#Gf!TH(v7+o>HA^k@FX)`#7(T`wWw{D%-Y&M}^YO)y*me~_1R$I!s?YIKk$$`)ZyAKEB;=5?1{pkX^`(E1D- zXp@XSuZB%pks2h=B?rqtLBm4O@DynH8{}wqY?wxrHYvo($JhwHNBWKZC;mqMd-dwo zmN*bkJQY$&1INXgj%(5K>#ybV`R--%#)42Onj0Y79r{e0Wb}CrY!Yc$?x5ixkfVhY zL*&W$3L1#0_A%gZ4?ya}r9M&zX3d(VX>gXW z>a0?lz|L)M|YH-)|e~3TFe!zeM90z&nrI)ntMV|)y12SdG zlu8<|MaQ+qg!s@VZK6%Oo<4&HrW$x%)1~I*6P~r%4cjSRUS49gS_RK$2>rUWv@}Ui zPnQJ?7HGZ3mXEeO^o44og}mq7KwCv$i2f+o15DKC;b@a;NC|+1bZf>d@!K z&;_QNkTut^SJ4>0{-1HK@$b-~LpMCylZc;Y{|0`v9*3Nq92qiXh~U{G`TO7hE{G|~ zGtWGu@!|TO{3Z>Y3ph_uu9&%oAq}*rlta#is~14JPv%`Aym5I)}a;$AbDmo!0t{ap3%H^qDrvS)UJVT}3}|x#9J-${sy>JcDO? z^gamtcIdL;8AZV}NrF8sY1giuz_-@85D)rG&Y3irZ0KOg_LwPCrfu5RcUO*)9e)eP z{9NHPml-o==y4zow5!-#mXRYz z>gyZ>(qJ-aFza5fDd}TV?ujQ$$`ENY^2O`)spxaVmiz;IPu=jG;UcafZUleIJu_vA z`^}_@xN*-J&)#YM4Gj&I;NW1v(f&AgVA@oDj5EBz4g9Z)KvW1wu#-F?< z&q)jMrXJA#@H*EBlpp%qMbHE40Cj@x(gp+f%V=kwySsZZ#yto7PN#u1V?K=Q)Fx^F z!N?%}AH!GTQ&Li*@xj;%d<#BRtn~roEs!@Y1DvDCXVz!tIccCBB~NMpH*DA-1qB6q z4t)9Lmo*&s2*9V)ux$@vj+_D9_u$$SK$>xn+Qx616JPp2wExh@g*>Oeu_P`WciIKW zsrDrhC(*VQx~}Ip+8W9?Wsdk$52%+M6Uqt4fcsgzZxWB$F2{)D!TB8Yz5u*wHyJ0a zZPNG~Ic4cA!=%Av;zqhCL)3*Qo_IoFvxM`sb0Tgg6NYw%wup)H!!;2zaVK4?R;}V5 z<7&KK|9f#KjT(RB|AXK2NQ1G9h89BuOY)nx33InzCs3w1*E;i;xbmF%b8L(c!MG0P zp0Y%qQ-@$PmH>|%!MzfHXZfd%a2}7E#)NcnkA3v$(Sql4wR{_%JD03SS)#1-I_Ci5 zZsH?YBdz=cxNH2`hgtt(?pa8=l<@K4v?puAULedVv-|ABt#^)Ke{d8CE>XKCnQY3N`{p3=Th7nnIVyvA{$ z?WCS^%xH@kOJEF&w2<%U<3{Yv6^yGMY5a}+!&hiaJ~3ZwOz7X!{~=wB%P~{7Sibq@ zo7(P@2h>-}JMpI;Kpxki`#&l(&J;8!Kh6Fp>_p}M4{6}sN&kiRk8;5Ih;t`-%rz%@ z&vBqHV8(&xwE48Pv}0IP=(q-G{!imhI<)*d%SLrMH|IugolD9(&sm3KL769?Nh4() za&#F!!fA{Z=T$yq@q6Yb;&;7tJ1X!Qg#VYBeel@_KBw`AlVoCYDVjR9T-BG$K~txm z!23C+6(x@J`G2mg<7m&tQR3|v^oR5ZOVgfd-wE$bHjndNj`GAGDKi{+N=517DC;=N z`i`=(qx5i;L5?!RQA$PW!jjKvS&)N~Z}2&-x&Wnr%S?H`s;`KVo{fxMaJh)=oOsTN zO7;CfG*@F{U+!27`eDwGjE#*Q3V%Ngd;TG~?uSgD`%dIaIc3|QmWfP3tY^Rn6)`sM z1v94539+5g*c<-f2aVFTa3HOB*Q-~rJJxn7(CISTGx(erU~j+1T<|UI(sB6bIoKzg z03SFE_5G1O;cuK9hZqOrWZVl4#{Mm1-i#L?{ZXVCyK;CRiRnAkzgoHnHar)7meGFF zMsmGG9GFcE!{PH^hF$#;HunhZ=BsR&F;m7H8Ruerg?r+R550e0$ygg>JbZqEF)GG)824w4jp=H{ zr6%5bQo}8+?UTlaBrV6 zUn7r<_cG4d8|C{|WzvIl3f8YN@N-8H4}%NGpFS{g;rz3GX@cI$Nyp1su#tRvz+VWiTbz_ESei2D6LCh04@Od8U1Y@>LhJN}L)2B~Q%*e=?Kp&LZ z;K6+p@`8!`>x?xq#=%$_<4lZiFiyr86Jr~UkuknBV{Ao!c22wy2Q80?yPDWH+jH)p zHh`JF4YM;I#D(L(`-vc7ylVq+;GQP;#u;OwJTlJ3_zGizPbf950o*p=1^BaeaL z15)Mq(>FsLA_nhWj-V~zdY}F|=_W4Z0n5X=@j7nEcok!}j2SZayvh+Tq)sqC!dPJ@ zVpN6)?9AClGj@wqDgV?hyxW@y`#*uYNV~&z0oQiq32hJgasKp2vj3maI!?g-Y04vG z_}rT|I>9&-V||R1WjJtPXVE1Uas1Kf`;(t|rP2m@(7&V%QV;3#vm_qOP5jsJ6WU8rSBmZ2FbDx8Iy5up}SI|Yh-lA?2Co?|IW5cm{BP&`; zPac(XUw&{!9-$MAUsE2J&w7B}*Es*fw$k7BVE>3kd>#};uWqW2P2-yi^3+pL>Gd6P zqFtmO5D)Tz<3ry3c>1`!mpxHlnH!>?1u*iMGrgZ|_*b%ga}v(U6UBYqo$_vl8jZ??&CCJy*cfZ)3VGI;P{jT>=bW*zDP>rrm#C(&N;xkT)R zXQN%N6>5{T{iXfEbMzC~UJ3T(Ip@>nFt&hi7U&o=^@8{@kq>5%m;N$iD)iey!wPg- z+rEjP?%&8W`N~W@nAsoaBYY!4+Xgkbkind?`7@p>|6KGeqd{~{jUiR z;{)TJ1Ht=_dasy%A@`gpkAI+V-9PCy^DkwI^s-H6+C1tUb(?fj#`ydWpS{3)0op#? zAl>Yn$%FJ)=esliIR?zMy|jHCXM7Wa@^=>dVNaqB?w{Of;=3kpH<+nk*RyX|#jD%{ z@9er%zOU<%30@$(Agfwr)-y030AUWoHIp-N%q*ETe1j$PH}CCQ)YTH6)neNJBR_CLmNqXd3pLe>eS86&5gi*_RjqLd_A6K4&yk( zrhnkv8RKbq{+au%m(a!u$lrPDA=k!~*8;3ThQY7nbB#e*TP?>r5NWGvgLNs>^x=#@ zV|0qP%!+jr*G$|)I1IO@3!dvlGXAk*{D6H?*JziRDX)ZOiox=PYv8RFE}6dO_;Uf} z7TPB25pkvOK-oUHV~u1bRrrQu9qTu)AGvlz;a7(yR#W z?&)wJ zfonGMkK@ESh<*^$_Q%7uzs2(Wpk7bXccFCFRBNUsuMTgS!j0 z-#-&;A4ejwo5?@S3)*fmCdcOo2y0|+{j5H6=EM=rPp+Z<7O(q1Z{&xvM_W(3!?DD> z>zb#u)f~&u_TM*1$nG~Ac)Yu!eKp!I$_?!{eJ+j%?Wr>^Jmy?( zU|F)h8GrK5_+2HKC)q#eBd!zJHtjOz{tRb$+G*m=cE}6d*ZRacoBK(GlH)1hW zVA;;B`t}evWL3wJ70Rn+Q-}4L8?!FHPpB*wHOO{{CEj6c7JR=Z zMomVYvG{geEPAu3;VMlZvGQN>YO70S-(k3(hML4F8MwuxkHlKF8IJGc!~$|Mpkjb0 z`)2Bd*GsRoKSKAL=G3k~zO>Hlr%Upio5QYvAs^Cp{Y11l$%(>rECafreyr|OsR{Uy zIKEnn^KrT+Ig<>Ykcx4jIvs8P_L>FGbSPe|v|to!&#wTD+Ym?F9691<#*Swdqfi~k zV7LVM5-8x3@cW*V!Krj7itsDH*TjJfAM&v(e06Tt#7k~&yFhwl(6hnviiB|!p6`D8IzXKaf-jEB_Sp;J}x%R{5Ec&r+-I3&px*`v{-tlr%q1OzoJ{q);d?f z0WvLi>}32v?R4Fa1*vyxY|3Qx68lJWYWx)Z{^_{bw91>#x5K9al)pGUD0WKhB+DfJ z4fKpji%6W3G%+^S(=s_eWb9b{Fm9k{T+F1jSkFGaeJk6p3S8N~Q;pyB-rKhduDyLL zi3WIYU&GHn(IL?xVPO%2`;Q!n1CA8Fax|Y+3wli*#X%DvPWi*&z_b&KYK~`y6kP)`?8N^pUy7JcFl3m@y_8dHyj0wTJ5$v z^QFakOY&Cdt;;LS+m^R0Z(rWQykmJM@=oWK=9T3so2$*u=5DjtylsBA09&wafGyfK z()O@zye-)_&6a7KZ(D3zVq0xnXDhUAv+c6&vmLY@vz@S&*_7SY?q+wlTkPI;KYM^Z z*gn7>Z69fW*goE#Y@cS&w9mIMwlA@-wy(1n+PB$v+4tEG+K<^!*iYL_?PYeA@0#zH z@1AeT_s;jr56BPBACSK-|5$!mzI#DH!L)+K1xpH67pyBNEZ9{5TB51t_|!?xw4BVG z`8kVomgKC?S(j6ovn^*=&c2+3ImaBFQ@O6WX-}G()^Y5l_~bE3F{zJO93aUtFtOIO^hc5=r#Fm`vskT( z_~~t{)e;zJ@iIMo^@MS^SkP$4nACAoM)^Ht2}EZd(K_)x-wPANQvRKQ>RXyI(4eL;;wCiMGyol{&Zas_TfqYJpA{+|A`|xbHb~c z!Yk?TT(QqI@0}|a2Jc_%TD|7M`_|m^W7oa+Jn+DSqU(n%U2CKVT=zfVD!rr9_2UOu zth|2c(2Tr9(NA5t&2BDL`2=vh9AO<+De^2=$}gv zmS4YS#XaIZf{>Aqgm(N*!QV0b4f^Ln)z=$f!r^I1aH3)=lNe*rKaU_ZU%zJUntKt) z+ln>|cjCo%Iii5`T)$@Jss{o1@0myk4S0EXeFttfQvct-{|_jzNbRiew1NS4Gz_05 z6uzl=d*xc2AbBHRr%#vck#O%NT@UJz5kcY;ANvDFj(j-FNbm)xT=WR+p`nOt_W0P8 zEK0P8OnSD^?h(|A-okg706sq2ikj34TcA*nl=b=?2UD8I&k}qKn1+r28~3R^yR!lj^nQw?s+{dbRh|=(1`mLGGLq2+l*55pQpy9$cP}GL+h0rM8RRhgu4c zx}%OKT7nA!v4FXBT@RT9y41`3IS_AnE*m8XPb*%Q(%Yx&^5HyXQK#aKyQ8%hr8Zva z2W*_ct~S75vx4y|(HP0bibhZgHnoctqFDK`%N-TRsa>Izsz~hz=bl$+9aw}7MCRoLu4 z?|8B~xEgIzq)s2ZjiSAs`QGkO3TmtZ@Y4nkR5g3YCJ4YrK0GB~>d2Sc^UpnOF6;>j zerni!qbjs1!0tswy!f`U&F4=CpFsIO*7*&mOQdwBzVvP_vqp99--U!4_b@T7+#Ox} zrDjpQT~yT4(a7%Ys#?aoR_?U>L)U{qg*}QCXIB7;sw#BqIDasB-7JH5fPu}gXWPIS zND<4lhXTP@P;jFzcwOF6oJwM);=0wVHNLdYC4fjm@{PtPtTw(Sb{ zNOnDY1_8uVB~uyl8T?0MWB86>(JX30dPqQyTtF2zdyMpsczx$tbiOg14l50Lr|||( z26Gkafq+t)m#b$_rAkgmO7on)&}uw3_(JKGdiE4VqgcDVG0(YLNp;tK=<;JJV<0x3P)i8KVWg3Eac>rsLVDD)X(b9NGWK@OJz1$vbe z-a66{&N0e`bmFghcnvo4VhT7Sh;|y%=NJUW0?=J8DgD$Vy!JAHD$&XMht$8~%t)CH z($2A0r~%C<$nlBdn2^oKB+OvMx{@8hy#}!KJ~9kdt8H?dO}!L*hq|=d7P1HTQJKsG z-YPsAZieWo44y{R0`{wmx*mBX$FVm}KAb}pjG(edC(0I+eOnpK?Ir3<07vWPs2Mp3 zJd?n`z!2c5d|o5pDyZkh(T=^TlyD-M0EEmn#i`QgiG+QL1kqO5T%)8SHNcjFAu2Jz z7ow)IdPrDY|2Yjw$P^#@<^t90tdZRlrK^xdo;k77@kDd5kz@4_Jl(tYXOd|cLd=3%B8 zn2SgxXIs(5HS+X{qBZ2wQbH5uW^2^~A3Fd@qobnXcC_&b*k8+wtTt=I2#4QbV&Nia zaCORVf;8m%L7F}MA+YLXUO@@HPZVv+ZUz`_Xf#aEA0kp_X7x#WDLh)E*k?z=T?qTy zj46z*MElivVRKjqNim*W-%yY4jAJ}S9-|qgu%}9W&mCWz-88K3;!x3EcQHduo8>;T z<}1ytevOPhB;Tj=Y^x|+Rb?dH4MFT{OBM3Z`vW0cF!l|NsRAHMBD?U6`yAz2!ShT< z9-?!DM476pBD?8XQ@ouX{XDZBb2O)i!87Bf&v{Q?8Qg|K(C0qZb)Jg=^D?8qRwXlJ zSk6;-xmzX1vs@8uPG&j4vl#F*z6U-M?j%zAmF@IoKf;d^?!a$hbMbb12D_;!V#PHm zied>c=;}+vEYoO4ep_&UrFY3t+DH%BSCbm)}c6+j0Jn>N^M7BGX#qJ z6Hvk(m9p4}V+0{8jD(zFKS8jtS$hN!lAWsp&^$gyM-!*M^)!*>;{Y z2RXH)(2Qz|-I9wn_7@lGi+HX-NZON{r zLN-{@jx=_OpajgPyckT4HR>X}W~*_(B@UOHAsK8n;iFPlO|esiut|WCQYu~t6fj) zZ7A7er9@~QhpYleL+*4IHdh9Uy-r61t;4`BVB0b5H|XjFr}z-u2Xb$Yy+i=D_OLE~ z0;MY}QqjcgX7)p$?yu}|=h3B{Nykj=3dWTl)bl=FyV zFaB@KZ>g*86_$!=YDHYWXZ1JBApDI+mXxDw1;6w#BmuRwo*KgWY!qt+mnT|UgCK9I zcCT7t4<8l(oc}dil=-a|9Y>3fJNBBs)1nsMBH(qB@H#HGa=Z@Zw`e24Uz~A?Q)CPR zG$zSOm81Y%YG41LKOmP74+>Han|}kie>{8YIxLWMV9QNsrDIu$mJ%1x%wDVWfNNJVEhpc|3 zh|<{B%MwyTV-_!MEj+oO%GFYK5WHeH%PlVXkhT6o9Yn^)FG77w0pSEhKt0qFPf@Mm zI%sR^MfvjyEuW{VR{e{)Yu<_kxh0RM_+2pB$P*)-n{lpa3 z4IK0$s*8<)BpoDNc>CO4YbMtBEl1t!$Efe-A8EOeBDXjfu$m%4sGn~a>d-VTLvC|n zVX*|%P4*SUiX6|X9Vs_EeXJP3P&Dex4S0wYuN}M%-JP-w2qNBccgvayCA`9%`sH?g zv##g2prO2=Q9!+_y4A?Ld{EvB8x?sWt9C>p4@Z&}eiytn&t3^pbEmp6&sKP*X-S^_ z{2?eZ5D-ln@*&erZ;NYWW)g2QVx=!+W?eHppk8YEi_P*0J)D+Lw6V*e1Bsc*93JG5 z{(g5W!TwdvD17@3y{~VR<%0aRUicn$-lu}eR4=xxKj=mISKg$Fqg!H51nmf#wIjaR4j51QwJY`hM-i$-ET{y*gvDnsDP0O zCPz>eV*i0~afNN|FkUHJhuF}>ST&@g`|VA0LhXeo7oY!Hj+@uq94Sq=m5{At{Rnn| z3O?*^6?3D)F^FAl7}O+MW*{m(DiA&7W*fwqdK%JrD4W3Rr6HvoK4KV%Gulgj7C0j3g6Rf+uR=wmty#|IOcWtlZvDXk0(5KM?4%Ubt-YN*!Y_ghWnrh?u zpFpBtQ`@W7cE!Sga#we+St8eV3*vHQrt=&(FRjj;Gi=Wps}? z5$vLS#u2^>wX5E&*y}Xu)M6owZnjhR*w`rGk8WcvAVO4_2&`j| z6V!aWOO573WS^Iuu?8c?sdYlR+@?dhYzH`*V>*f@r+7oLlqFtUEagbo@zNbAoeVPU zRWyJKU%?B<6eF-S%Gk{QiU+j59AmgEM9ZAZxaC7AwlD<_QW#T^9SWnyvpr8z!VnVu z*|3U7op*6Q%&Kk$s=El)BC7F>QcZert<8OjG}~6x{2tbf3GP~hAlN1LCaQpTP;KWh z;#sBE7GO~fg(@&-&s@7ldN9C#fbQTVA1lZEpnDx}xtIb0@#%z?Pg5=SCuz#kQuc3v z*48sCZ?kj__0DJl%~JUk(>|f4J=J237=ZgYpeL_R%wi=27`2n>vZ6yTuI`Yo3@{CK zs?da-K8$aBfPD8rHvz%He`x;ZTQu*S70{6jBB}qOd9l8VZX8^G5!~*UMJGBSRF7< zkn>6esRF3+P=sOJsIXx?k5lP)6blRhUc|BvGWVw-yJPRL0O?HEJNC{*wi<|n;VM>R zhr~f^>@FA)1VpqzlOG0X=?^t>v7l7+iZdV)9ebxk+ozn_j=eWh<~G0{0<4+r0myud zAW>$@1oIuYW0>%cCO|rRd-Ge)pB~$MrMGt(EO`md*j@?ogxS=62`uvr@J+PwRs@M< zR)U6DmKC|FgQ{SkEM8`X#dn!CWUBPD-`~au0Bk|-R>#&$#K8ef%CtEl+4ARFW0Me4 z)6_d`>goJHD%IURhb(BzDPpNC&PwuU6Iwn??J2#qHQN=7x?|7NYjs?e;`uF> zLoJt5P*Ws#J8>n}d#Z)kT7X&~h7l8@BF;W5=Z%4Yl3eOs%uF`R5iPxLdWK}ty*3Y& zn{(&q+65OTC=cb}^6@{7OyTB-Q$Q|lI#(mXbL*Yz9rm6Un`k@VLKC8BQRhM;qvD>@ z0;^S|BB5wO%&FdPi???vDe@T7$7x9a5bYx^-iC3Cp3P>K{syyO!zNBOO(tP51WW2F zTBOm-wUA;kk$-0eT7}GftoR7p=y+Ozs%7>UWXZ`(G^k1C-Y2(zCD%GlN|{~C^s_%e zPMM&et#k@iel~tGh+1Z^YG{7gCb#zjMjQEpNgV!yP0W0enkl74%W_DQHs(b?>z&SJ zeA8UC=qO|*q=n5qz=ln;8%-QK&2+Bp{);KX?uNf(Go<6 z_p!bo2*OT=y%m;&5PCVCHG=2SDYqM$fYU6#z;+Wp3y@Z&#P!P>Uy@r7A zBjMc!iS%W9QcL_fLYS*GQMnm%0%F0e6o8TB1}7%r8mN4E2p0 zJib7#R@kfq0rrB8w;&f>Gl=g3@_RanoW-u=Rq<)_I3R~awbGt4yDU!kv)z-ZTjFfm z?Rc`i&;op{20Z`;gb%g%bZxj=mJ1bTh>wl@3QefV#jI6h7iitbS*w6(n1d>4o*@em zOfJds^m|m7U@$*|#P>r{wMQJvi-6fCk6Php|Ni$RgRvPzz(I^f^R@N?iuJSe1eIi| zPH>AEtFzS*6vPwz$0wJ!M`5w5g6<#63i=4SM^JTPPjS(6U_xn#ADdWMiLJt9w6EeW znz>Me2kSiQ*=ajwAY8wXVrc(e`eOeOh}N3o#vH^*XXSk&o|)_3FFabjiy??Xrc`vW zyTJ9}Fk2{>k-lEVbQn5#gp0cCg(e?0kk+moLx9 zDCnS3@Oec7%Eq=66kCoC;@Q&KR*DFj*uB(DFd-H@4^z|*8cREubnNU1(%0yLY9AMJW<(y2BzU8y*Wea_$AhEhP^l}z=XRlMzTZHGYcpTh{p z(g2@eLDk#NR$)J(m3<6^V^2aJ@>#CFb265RJL3}|`iFMYZ*~{`j_ah~B1XR@9r&%; zn(cJaW2lus#__W>TyJf30$i0Tz~_Tp9bT6YR~heol}PVwAG8ciuj znhF2ypv0ZMpkOqm3%}`Bp*fn;jSxD~u-Pl&(^$jrXvA{eu)yls8>s_4C;~+NH?*h< zvrhH~Lw~f%|d%2@=TXV)@nI^k60kb*N9ij@%7>;wgr5c7%bNy2!-Yzvmm@?0!_7{g=gf7 zUXzyoS~^;SpxM}fuzw}|+lHWEDiK6|nI>gGgaX}LM%XMiF$ZVl_ zm&`InZ#n1yq_Sm}>IjcUiRW8|W)Ryui4zoFv@pQU9;ZI|F^cn)QST+57pDV{0DLl%GV z6?8glUI>(F&)*Sl1d!a8Isk+oERiJYN}eSp_&Rd<*`G8%&M@ksYGwcpOw`&eY>XV? z$p;4~J1N;LXcI$e!LvO1U;2~B%59mHY!U|XOCdH(W{ShvJ(hkZu_CDD2J1i&T5Wr2 zGY}KsXO)C`7DP79vo5UH^ptjt0J0gE+hL1THdvME$_AUVAy+AP^0jct8C)$uR4hP| zg=e_6AAJ7&MDRIQEHo*$ySY8i5qS&L;C8o&bysnYcsH3vNWUq6k;pF1ij;jL$DQkk zN6KK;+HnO+01X?SNaoU~?((y5Ad#x7cqyuNSC0pCk=^HK3;#yZW!lfwIOaR;-q3Vb zPJ&Gx%I$pC|Aa+je(*UgNs?J*ZXv6~;0rhNIB5hbU_WLkh`%ejyR@;W!vG{xnvr$J zF4Ukbv%4>eBkS+uHaFzq^mq?}20Zt=alyoIfJu8d0-#`w{*KALfteoB886 zujBE|hS&fV;pzZwQ2%)bXmL3sK@X7(lx#lu+Tb5Dna zAYEz@S1%&c>e-FFT+vdkw|{$e|65G0#|oQ$^p8dH0>{!DrP;Bf`1gqc`^E#eN0o0>o^e^Zt@(3$**w(;FrFl+eRh~0~ zzx;M=9dl;65uQSC`jnLn%Ogn71na>I2X?a+J1JkQTG6#a!CDdYTt+6hzg90WNCDjqtmoUYw`08Pf5E#K z8$H$P@#(#+r{C0 zKQW-buO4ClWJJTpMFR0#SoNSk2V?aay`!1sHZ<^BOqDP8iB|XD*Igf(x-PQh_fB;PFqR*&3evHliCQto#t!)eVL!tBOpoBRH`T^QSWY`e)dh1(8C+ox#sQmIZA7vw{Fj$vtURp6$*B@Q=x2yA9D$eaI$+;GBiY zoYb;y5C+_j<;j+vw7;dcB*r`0hQzT6Be~maU+Z8+kXgyisOnb7Z!7HBCB=%!R94t5 z_qDGd;Sbr8JGHd!g%N*~TtYiuf|%=P%d#-o5O~TKAFDV(Y%){MU*_Nb9~~6jotwSG#xzlB;1Zb_Y&hLlnXm zpW32qvMQTw$|ifur_LcQkxkB*UV3T2kVSlL2XOwoZ&1%SWtkeCo;#%TkuBr!dJys( zaW=%wm(DLsNYMJuTrk3*`6v(xGgv%*`Z}wg{REoKcPD6q?nO%qn;RRr*P+K9UDMqZ z{t}>VVVVYA4b5UfWcyc$aO^qa*kf@YSwAwr#p8=SF_h9nt~*&angA4==9sXv+R!YW zLU*kr=S*ZmeLmDpps)mn1U6>@sykDOc*J6|3G^oikg1aO@S$Cr06;$u00g<&gMdzO zpgf}6Rxef4(_#`c>*l47b2e>Fp<=aRJuPN2o1$D4g@PKlrV_!lw8m$6fZFV!!$`?nkx6`XDvY@@u zsafE)Jj?ywnzrP$_x#5+?ZMcvjWn#UU`J(7r(?9nckrF~xvRx-^5#{7I7(d~1asO# zF81%3Yp}b*(ol74Xei4icL6d#0R*d5cM;#Np9Y)A7|fi{7_954?;|b|(_qZ~g!CT* zQsxF#4vlO8eF~sS#fC(L_ES~rKm~usW_5C5-RZ1E&(P-0b0|g`my1ybfh3KOrce-M zz%cw33YuQsD|!>#q;hmxZqh_GXC6w1a6oN|r^KVl+Y=7S>_4GJ0$HzSIV(8!!z z*kq=|Rig0ZZ1A`8h*eo@FJ8nPTWHMG)qaU0-$y7SebtoNfTb50Kyd6S!$>(AdlBJ5 z#e5BMuU2%Rm>(T2fKna#PY-nx3=jEDWhM-=YaDxKI`%Zf=;Cc}s+)pDTd8{-N;A!M z$Jc#9PP1+1x|xD>937`)iQZ4G}P%7!5eN>wUt@Un%jVaO~)R6RnXO8d9sBH|NAcp(ag#fQehQm+4<;R7KnxQhnD zXE2h=7416PiiwF7{(BP*u8^o4O>wSWr*BQ zD>DoU_0qZL6Cu(C8*sg}^l z&_C=cTa88R7s%F=LZj2<2>%H$7$Hw*Cx_r1>&_`?AEw@&1^j8>ITg>sX4tIccuK9a zMx8gu2`4T6jRZF4>`4Q|rW`NC-@2yU~!X}~U4*;J+ zMWQ0EDR8Bi(4ZYx83}|MNy7hYXhA8b6961Bvi#W8Ew2MF@-=7`A1tw92`&cJEkrRy zEQO!IUFsGh8Qw_`mRaN>PDvxa(h<^w{ z%GhjVEJev4b<1JAT}MON$9w=#w~&$NjXM0~M}4e>M;%YR-M|ZL#v98+5T;;t3(>!1 zGWFKj;-?5FLigZpkhXg$iCsEPwMI7e_w8n*Z-=RAzp=7y z6fH-2S4aJ97rkEA$K)jD#^MBAG1adYxX+7|1Ilz3qM?pCa4fd35yX~Wm4r!f+ZbaK zTuUshMwgO*I{F0@@Ntqm55R`ZaxhfXE@J{NTMf-^6DHtXW}@iTs}i$t9yB(Zh3k<6 z+1Wpl^x>O8MdV8-x2^KCDs&i$n||v&N)WVzfPUObxuuR)(pnq9n5}yD%Xn~SIlo@C z8b#>YyAZ=&`N!%-GaxRE)vnsr5AX^Bv@LDjv5Kn17Vt0ni2Cg9Oz?v@URPAs{UvQ^NWZ99li2S zt%7|98>Ykuw}5Dz7Db*x^a0c4;OGR46Fb1#ewb)8->So_C*9BHoI-424{B;gJe|ED z?VN2!MZ6wc$jNdctiT6LTS3Mg6Udm4tsLNtZH|UG+M$-^p%Uza+y_boMh$FeKZd!%Ba18hjG|eh^3HK4rs@M4#vcsWYN(-=S2Y1|f zAdZwv2oO$+Fwye>W)CTE2aT+q zl(K_HLo|gl9+~aIJ_JGWyvBgsnHV{ah8DEV7>1Z-ND1V!^?49VFQV*f5shR0lmU}K zRyWEskTr(pP6Jt92m1^Rimtp@Eg?HrP$@+Tyfpno{rJx0s4h+N^D_`S34SiPoSy-X za>f!bPl2LzIWN;WoHVY_!GCd?F$wJ>Hx0Qni(E4t4UeI5m9%{uspw>F?-K`is`Inp zk?^*Z4dEIof1^geFnYbU2DVb{9B8+5zmAZJdv=Vc9k#wdp<2)dP99a_6!oVxhdB0F zO`0pRsP|6zc`UNQ*1M^}KP7Yt)GCXPN7zLjsgE^mp7F-gcVc9_& zULm}QE%2U#8ujCe`IKruLZX%;`LVrYAsb7<@*5Jv#;yd7Y5C%3kAsgPJ=qgjXZzXW zFLcCxbO(jsluc3VKKwJ&Sz< zkl;cFFd}gPPAE><2yS&WoJRlb+<;({*ZHp^p75%IUj7`S^`b_UqZScQLUlW>R3C>s za8NI5Kr|wtkAI+4!*S`f{FN19_oX$rvzso!@RcV14KFkGn<*QcfG8zRf8QvNqLM`v zSD%$qioK`BOe&}PxZ*v{OI53nYcEB;9jifu`r3|-c&r@;e=LaFi2p*&~>%$L7@wx4FBc;T5U<$x7+ z!u70S6#zpPHX3FW_>jRXC(VekQ3RL{!jPPyk?&F$4VcIU`+C@D(OJ*Wken% zwBQ9L@OYpkJ+JSkCL^vB3Nc4h`dQHFG6})u$Pi%nSMX?UX(j!OJq%KXy7lboz*y~a zpA*aAATQ1;Y;Lm8ZQPn-Ls>P&xpPIEr=%P0T*GjTi7N0#!j$G~tiHrHmV<`L2pCO{ zQCZ1F?1#trBG$s51&%~|F&q8xGkPK7B*-p}3=+lJB$R3J!dQf8Z=Hk*r0vcZU}a1S zw<3D!-{*kWBLp8w7dnAg-8yi-q;nq5h`a(3c^VjnJR#RoKU;-fsj9+OM~h^`Vms!* zdt{pcM&HR@u!=-DV!02kohCP@$mN&xny5z?GL&))0uzLcHqRA!DQqmiK`kP9oRE(A zF4ebD0dNa@r!r7eT=AKsArr*H@nCn0qXD-92x<W1p`0)x-x*=4T95Y*laP`|6&wFmOI3Mgg?jkRrZu$Jz}4R+w8s!YcQvJxHLwD%VbTzg>;sSt zBrQ?T!#_=p!do7WX_l$R$pFfXgD~FSCZVy+%6AweWp?B;b`~8Cv?SBZY_d0QovXtM z@6yJf7M@YhQ4ySMw27d@Nf33X*3GxpX%DrPS?l3$of7IP`= zL`dg-u4f-dlc8$e4JSl$yy@Y*habh4|9Q+9#>)=dDbw!q}!7aKprPym1|A&~h ze5W*WOQuGC#tSr1Ly6A+X^97n60s}3oTgYe_R6^DFV-7B18rzeJY-p>)V8}z=#Wb7 zLiIe~RxZxn1&e56N85qD-H$Nni8J7Z*dgm#8z&pP&&mDhvmiH*p-t<3M*+;=uxUM4 z+mTe;F_U5Fb+C)r9>dhbrkR0(AxI1}Lz!JYQunE)@J!tWv*dY^?0;f0HueJQ%zP-_ zo2CS?w|0cca{D*rUYJIn+Vb1_GGvr%tQZbU)mH4t82!yx zI}+AQML?!XyTQ*kg3q{&BG#G!cXz>qYP0-oEh_S{mrzgD`O{Tnn`!w?j$&DGQ~)i% z!iE#~FMz=hjhRi2!IJSZ7XulUa6*ua!E|w{DsUG8Kbp}B@e6Txa<;OlH%Uvi91fr| zyvG;WB%FQt0bxc&9}l8yql;^8QWot3pg(R%BuSQZI5^ezGRQ8WOlv5FGTff*2tPZ< zE5Qz=p<>|l08|Vc?t18ecd7R*Ta7kQPrQr-=%3i%qH;kh8eDJe!(ftU{Nr`3SxwTo zi1i=)Xbn7_k6^t(j^-rAifG5=l(+GHNO^47$ax$PBUbxb)hpF;#2o&Elo=ffNijmk z@c?mXKz~2Lwqmav*8)_*{9E65Iu{3*&T`0QYBN9((_F5xE##ba8(`-1rKM(=!~l|k*(^c9sol`rgDUF6vnDX zwI7Fa*#Dx1BGlSTl7sDUAJ}`-e4z}sn23deQ#@YE=d^&}GsLSjD!^WALsr(%p9yaE z+7M-?hUMpTl$7j?#b}UZvA6z-P_? zKA(Ne(XMWVTL2+#3t&2eYp>)imh94S?4JBPuz}emji17V=W1$yX726HdQbweH+(MK zm)2dYPM=fh4?g>AtYr>h%E1bXcK7G9cc`lA6QwHFijXp0^Qk$31mF_}U>h#$!2H}N zjfOI=!~ON?M4n0PamtgU!N>IBu{calKu-1(L>k9P*f@ebq7PUEfe=kTgN_7U=;PQ7 zl2-68PBtu?U565kV_qk)f>qo2-ZVdMkV1#MK2cBQ;|Qh=CVSc%!O33Ha)$){9P`iz z0APPZuFyn&@=1F=F^J$_wF!C!P#r^zjkN|5iXx1;N6+rygNuWc)3trwaI697$bgvc z!6pp0sMmbWJwz5nu(O_zlOGOC%h;nsTB>4S+${+Gv1!TJ4-m_XTR=SMXX#k=Dma%0 zKk*kH1xd?*W|S_nfqe_I94vbSrh*sXY|HX_(nKU_f5Gk^T**f&ORX>9^eUMJ)cJ5S z?^7}{51=seOFv>p7!Vk*FVbNrX$rd$!w{AMoRGD%Nj&UvcS%FhS~k8K6u>yc&f{B4 z5X5XilTg6XP)DWXQ1MJ$m4g$*^K3C%~QnSV9Uw1V94RV}R+mu1m*q7=g`NYQ%agBuBr<0F(O$O9?-u#B7oh z8C*(W|1T*h$YIM66yGC7qWy_nir|noq)3fYx~cEK5F@?NTN0kA|AHWz_}_?;|3Iq- zMw^qp(Vsb{B8mML@82UvezYHAs;|q@*TH3d zMH=FK>^|6#iO=aYpre840xoqlJc;#?( zp@V@?3#S6e7x%f1HaA~|teL9uX2@urnubMH)4T#J zR&O}E5H>RZs6Vq7tiMQOW&M1dSaQGbXh=mNQ12Y!Z(#Dnkvp-dsk9)^++lmt081R?_>c!lsifvT0E7(75v@gL`O#R1QkprL zCjEt(Q&flL-JV(2av`fESdy-wf^XAL@6s9%n?lws@`VJ-r7 zm>}M&ru6{Taxn`oh#BJkHp@^ot*Jt9oR^xSO>$RvVWCY4&!L}mYu zC%BA9vRY1S9@WuPdLx=NX-?z98&hB`*qGilLUlAQ%$zib>;=iUtLEgN)`p)y{WKgS zG5Oip8+`5O#4;woy6Xg^2@xLSU2v`&xVeW8`Zh~bllPR2rhOi{qLVxzp|H^Y)3DbN zg<~TSu8y#Z?gxEhvhh?$!4TDoBQX}ZJajAbMiyvo;E5r)yXn7W3i6GBlO1$0`2yJD zk7%%bVW>E)Mj1l4bTpgM^ReBCr7eV(KA4Wi(~UWDaRv;XWQcNxGWh9FVxk7h?RDa? zA?Fe^UAT4`Zx7;|Dtu;x&CM-oYsRpV39w5i`>T8wLG7g43Nf7&(dQtpA*Izc z$3dL2l-o^W+dh)XZm)A}vj?;3d&onzy~2wjVXEz|Wbdt@368wjFenSKmQ85zmF(wO zWO6OALmS0557hmbQ4Sp}OD+KI#09X1bRwx0&8uXiR-)McwJo?eo6YF2mwj>qMU(!b zdYl96gDgz?bUNZ5I#P)HfrcQ1u|oJQ;Bh}tIhU9tu~b?!44Y<<`!?2nJ$0{Li(=py z+XfSf)o|95r0Z*dU7N{TkUzOr_+4n^Vwy)6=Gn;y7pIc%hanoixA2Y}S%0w(xz}XM zC97Z-#qqOPW({;^^@4oSy5`37f0RG9i1z#wjcIb!B*#or4^Dlz+bk{gaN_Zn{AWu` z%q*s!dkF<+7;s+@94f#LU}>Ipz<2}u4;Tc8B58Yo%r+a@J+Fc=q|b9gIM@RIPCET^ z$SIv48A;q?AkD7~pzm$h!mx3x@EW<|O0G)wGIpM-6zpF~BO+x`!g1x0lDb&Ig$QL< z_{iQ$UaT{fr8!tfKqoN|BLTR~b9cfZWN6uRWzyBOoFNMm$`waL-@!4E`Wn0bB@nF1 zq3aLHJ)sJe?3sn5gQ@bv$dsqwX5BDE9oA^pP2@0V$5f9C*UtVup$EgnliI4M8YHOi zti$XyXk#VeT3FZ&4GDATbWlG!4mPw*$7?99C2p-!!dsC8djyZUkVnr8Pg)Jg z2%RbcZ5#1Wc5}Mz=JednDY=^tq$s-&<2M$=;uUq^q?-5xnOVeXxY0$NR9;Re!z_;Q zTS%581aFHS>gHbM0O8{9 zb3|74gIdq?6Ev~A5To+G|50;>MpK#gij&fXb)|h#G(Y|UL}p3lZeEa zF}f@EGLj7HIAhQChh4EJ5N@)}m?n*{d&D$V%E45V$O{T3@~#HVj6x1^lL7HOky+o2 zuHnoOn@G>eG6zM5B8m_1321mnH^jz#{7>}p2oA}`h-nWr3jWC~M z&mpJ~K1iW(b5of3t_qipM2;g6;rzyO;M>q-nPXJj05xhCA})jIxdc)k#3G1TCBDM( z_#UVaj)uh;;{3SdtLS)fp3G*6POwfM{%qytj_^xZDAXNtMZ=A#3^@dY?_+-CJI}{? z0dRJNpGDFjia(Cmfn+ITAW7w%4LgODvY%*${x<-f)b;@eqXS%yhCZwYU{D&eqXV~N z7^k{aezq&hr3fJuI|dk;fqE06Xan!f`Pgrx))D?15>;O6_f#YnIQGu%^>N?$h;cC^ z&Sjxuc-`HDLg_fSI3dc#7FDHY!LG+jI)fAj@<0X4rbN%69BsKArtxjX zwTyVEt9w}hmLF2ee~8tiQG!df*QjBVabyIv89^m=fJU*Iv_3T`&LxV+s134BPQCrLo1TM=J;g?+U3oDfEL@g!!9Da+r_^7qx4o|$nJ|Jiz3AbH(4$^5NY2&p{CZM;bVy0xtG527aYp^h5%-s;ce)jr{v?0TV1-0|46w0NmF}!xH_8 z)8C8pWpHR=@Jdr>}@UyU3I-ZAMP)Zzc z%om9bX>9~(Ns*SPF-M*p02&iMxq0M9Sb)|#&z~M~>ikCoEliB5Z9w^=dRj6U zev3UgFN~47R6cLqeR3IJsI5byQtB0aN{vY8aH}XMb?AL&ou=?he{ z&wqfy)l#5rH&_Fg<6S7;lxpD=ZOojn9f)|(<+qh3@B$TZIu%9Ya$5X~KLm57sqfYm z7l;9!O8}MswwVe%+O4k5A36=#1Z;#3a}6U z9RSbsxGI$^7EP8$t_I-j%Lp|>`hqcLn~ulUfK1<`I2(ex-yx^$MRLg5_Qrj1A6n@V zzQo_W8jtW4{&wOohQHB4kFjw==3YPhcoA9!oOT&Uw(1#XUkaS6*ixM_5@ zBNMr4kjLQ+ypX;NwzvD31-Ysy!&q*;Ox!PNEQ;|h0BfD=n|=oZMoaOFt!P$qDgHaW z$XFczGoAyMQ`#H2Y$>iLz*hHzu@MOVpO@m5tcEx6`xe?gB)n+5g%;W)2TC4qRQ7!f zZ5c_%Li<0cSYtsY5q4F>Z*y37!9i92HZU0dbEC9#e$nKTo$`87&P(B?J-4casy z9lKq?=#zugeq1KBE{i=f06HE)7$lZ~b^m|4Kz0geiT(>@u@hFK@{26FK=#^B#LE+Q zlLfe_UgZ}ykuyxMno0*-d}>Jn1_xbr>8r$9Byt676=#LaxB(v9UUW917ZC+G+3tgZ zbsE876kUs(;ot!HAP7zNhz;5Njwalvw+A)?A|nm2o?@I5gtt;Jd*;_DO4HzBp%&3C zQTR>)F%zw!w}XH+a=b(|&GoZlkgzHumL>0Q|Ew}(of}|tfe9@3I59={Pl0Rs9bzku zva}*UGa(<{>QNQhU=k|a0SBL_@(o7`%ROx;9R$VqSN939sC zJW?kSW&#ePMN{ayE1GxUSAdhytvbK=ik;$6gaW?_3Fj7#iwk1td7R>h|5Y~$oh~fb zzb329($<>dOc88`i$-ixJn`(R%x{YFF0rs( z`;6OJNbq4Nsl#VTKGC;>JNxySr1YLTVnGuO?YQhKx5rb8EfQSJupgiy6AoSMqCB`@ zi%vw-mvO2f8_Q7@D3P$XWB!D`;%5R};9F=Y7o2n?2lgD8Ds5)S z$Bz)-FCTx77a8(#J)Q&dk&wJhKK>{H=IaMz=MMbOO|I#?fy zNmTqjhR3z2&ya`DQZWNIHojdbj>lfx80`G9*iLT6I*-LFxIjrI>sXnU%z+6n995{F z&aXANR^H&WNO`zjw#1e4i_v0s$rbd-ESX4;v=YJdv`I=~yK(dazMwd85qxi*2i`jy z&2hxN5GHxGy)J*mFm*v%KYV63d$F3j_@ADhVrV^O-tkz z#WrY^_WBD{{>H!IUYJcQN`8v(DoN?lvK2BSwM`{RGv4dz{ecpQN8_FPS6f>0i{yKl z-shJ@lJAew`^*x|1O`0qr)bxg{5<*IMDOEEcAFFF$S7!;C9lvs?#f#ML~tB^1rGe5 ztWq|ufWI3WxPV@kF25UcgxE2805XMr4F?B^8oG+h5H&d@YDkvPFa*tF3@-?pR8vzb zjJaQMDf21L5|R6&QnG}kj4r-ylu)S^`q|aUP)7o0F$ow`CHp;{JmTh4@m4=X;WIdb zjRA{cH5bbZ%Q-sadqn3bu9T)Z^FvTIxtvH&}8m4(fI zB~AT1uDFcSz6z%!6ykk$RuZ%rPDgiiXgq}uc3t-=@us5aZUV9_HN3#f*4LKXmh&S;Qjk5Z%`6bbD1$SWiAc0$>D?&K0wJfH`Y#Q$W8d5#C>}>gZZX;) zgpO&r;yYn>_g6NK%gQI0y*LK_4!SH(DO!b|#?+dIwoT8GEVx`wUDQjvU6qxQ+HRHs ziAKuGVS5Q`y>;ymX!GoXzIL`6Z~5FDu{yA&Jq_1I(Kb<66@1XHNo2S51^iUNQBuZv z0p&aCA~}U$Du-PYath{?biz}{j&nuE)OEVB$NjN!zhg~tVPfhkNK9P?QWw5+(~Ac9 z{r>z`|B1NASLyd-r_fLv+QjKT763Y2XJ`|z^<(EHj%~_rK#|r!PQATs+p`2A_2TP0 ze98lN(uavCoX{OGmF`=vV?97Wf$u$M!*9s&?+X$X{ropjbo!^$$u|$=m2u9rm4P?r zf984ZHHZ{k<|qygl!ik&4>OQ499`zoh4Kp0S5!03G58AxC6GkBK2Q=;*tM!QYtdGq# zc-ImB7&fSVLLKH=uTvU+-s=?b(I7g*b5^w0Rp@otp_SV$`K|krxtWZtb>f_IadNrn zVjp7*M9Gmeb=HEAv6HqEA+;^`F#wf{Zfz`ZgP@^e1r*z9-0$PTEdq=1;jyfcvnszu zycvJj;%^-OoHFxB&lfN1=EJvB8xPkh3kuV+5inE0jsUd;WmMx(h4WPu3>UEdf|XVi z0+QShP?UfcD8OH4P?ZQ76*oMM{sf(s?fAr;@o30COK zSFj%f3)v+oc5L<4@8@0p8!VQ6(?bYZcJvm+PsemCRI>a_2we#Tn3FX>Eh>=g`L_8fls zol!A38Uc~^RgcqFS^u@jQ;VJ-dLean|oU7 z91Smkdq5zwxElV4DF2sVpCwUe9+G7x9htoRiYgV)jUGMK1P2Ob`HI6K1I@d_En1;dpsC{gejhi55R zCq9HN!SKTzhT-FfTOL3V{j?4ade(LMxHH2Mz8g`FgWkSE9VXoIc)^CpTs+7#vJWbz zIW`<`SeW6)eAZJy#BmNeBp$=xlYs zvlxPtj3fLqFvIb~uU>mYkQP&`xkDcvaRP$xAQ7OBE%$@*fu!TH00N2HHzaF!G|*84 z1A}{w$SV&4gD~luu{2Z%M}sl{AG&>@iaqn62@!&OzGKVKuo7ydG&T@2 z17-pCzY{ng!W7KOKa;ofW+O%WCCEaUhb(u)^(czZ*Ol`4r(WNQ&Fs$&|+eXu<^ss2(q927Wy#Gqf9nK zX&02xw#J3=tPRAF|5Qd~=Sg<~@LxVSbK*UovfCT&JXlLw_o zd<#cP2K%KG590oaC2{Ice1f1o>BN!^27w1Jim}j~=>iV82LT_XD6Z`gCl}YYi=47( ziP2RF;-bf_b-cw_&PI!kiJu=;HGK5BpNgGbK}>r%C$Z8b=M>V&@Jb4~jlPqVjSmjh zkVaeMHsjbJZUj1H);>d|V{b-&OXAu>es>}L7z@@4TjI846WuF{(q_%DwA4@Mmn46M z@9h}ZB$wwno;ai)x~z!)1#kHb3ygBJvMT+Ky$_`po(y0^oxZ^_7AFvJh{t_lO*(GD zv-}a~i!)}+&69Be5trw1Z{2=mlK6!Bg5~Hx<8H+rpr_!IJLwCSTv5Bx8^?u;{kJFL zW<`*mfPxTB0=t$|2pcitLTKaHQ5?2TDaFTA=%$fdR8L+Dn{XcU1^g;|(aE^UXy6V; zegz{w(u3=h3s2V571H>$B3e$jCnvz^(C@c1P&=Sd0?$Px*Mn?}2Xml}&AUSos?k#1 z>-gRK`fh?VPnKHVTX=*m{yD#|&#C$*->LfY?qpeLlziCso$LBg19CYR`9P>HRFb%V z((r*fOdq_o8aGPX%UO`LxPSY4FE7ftT> zH%-7uRNuO7dJazZ;zENS`KYeqTUq7qL$xN4;?03BTwI+e4MBI)g|$}2o2M3$;gWpe zC&MTym?!gNlSkvkEc{0Pr^Ob+xBo?H7r!ZZC{u*bJP!tTMXK_!`ygq6v?tGP=0=@tp?Zxq~xuw@9@Xhq5-!HZDix$WJ5W-7V`!vQ2alv==9u zg3&bkd=NH-wJ|>SAHVoE@`jlYfVW~*hAO%^{swv&FB2;(i>qCdwX#x6#jR7^<3An% zVe|BCTJxa=0XF}ixboJ`ya+%lS4CEK5ZCi>FmHUEc5)JHN|b9Odw=fFFz}?w7|K*q zqFf@HA?$qYubAiL!+Dn(;uED@_Sq*|U2`tT9n1x}16<%DF393s;2hwBT;c+-0A!xF zdDDz~y$ci7`l*Baeg=*Ue!K4<#5ldY@9Eky@l_n~@P+U>Rt8UT%<)7YY6)=wY62OD z(J3OtVj^5&P_2^XJeefcz}J@U`04i$>nl(YWa7k1oZCv0Nh9s&aPIe!iHyT!H@p`b zA1-8MH&7|CU|!9ib~b@Ooop0;W-$kU=CCw+PGbUpb+I@w(%0p&F8-X%7=KP-?fhB5 zPV?tfcAP(R*%AJn&YJmi2HS_HeAuI}^RVCWs8aSkf0ncD{5g+3$)C74fIk!_ zor3?tgUuA&$%BU}_!JKwp-lkIR$eOT{MHo;8qBVxx6Ar!x!isY*M&WvJ&~qjFO!0 zl$=D&R3j$Kosye~nP|l1xKmt-7^e}F>rTl_#Pl_BtX=qwXdWG(HVA1DEZ6?P~Yu?%~ zar*GEEBPHK?5X$zWYsm!%#L6uvCCsD6V@SwWkMkq-LOwBzZpbS^kQnFXFX=>T{tQ?xmsnp6+v%$<9%IXr9 zl%|;E{(rywoC6m`vwH9M`~3g^cVOLp&K}oVd+mAewNKi2xb42U3z8?SeoN5BcSAJa zgFpm2c5#4LBIhzlCi;kU+LmqpAuFUcd zDl;uwjp%XjCgRF&VeDjY6hFrPy~+NaDd@_i1Y51*Mi%U#+>6EqyTPzy9sAa?bd-JD zx%JZjq0)a?uxR-P9qq-Q**JXa;js@phdp60{foo{7O@;=K0cQ>#*YP%1ZaB*OA)o9 zGj;J`wV|uUlBR-w8F3Q<%VrDxGt6`JYC^yx#q{d$BhVL!#!LV zSGXdM?~&#wfc=1X0B->{0bT&C131E#oh}T!|1?Y|Oef4UFwej&g;@&oJk0Yj%V3tl zEQeWM{~pd;V#w|Fh`XVHXw* zA#t1PhqxDvsRZoYT@-Sq;_df}w{rbWVRU2lr$efW(+6cpRh&N;MWD4~%?Y)M)7&xD za{dYI0DIykRFjrD=;_|fcbYqwDcS(M0eH8CI!C?; zlAti{2zRq`otWK$w~68!{*;WCvnMzXYxhDGWnreRB-Vj@a7|bkb$VG_55cW2j#Zq& zz8Tr$?26Zt*WV^iYxq-g^V=kJ4S!1NzD-is@CQ?XtlF{Cv{;Q3PC}>s{F7Ly{|vT$ z!%y03LoZbq%tH5t+7fgmj=Y6Nks61~?U%iAzuV<{xZmxvr|lNUh`S1-KPeo17wl~V z9V3zoqYv&KoWve3Z8|&Z2ZEirA<9v|Ctf_%XW!^!^P4%MkAb0%_z8t!4ZUUfv68Qx zrsuIt;^jKe#W-5Y*-3G7^vQ8J{x;Fu0i|-dSqd82&`Wz0SnXDBRndYboO5+Q*c`$4xS%6BLtf(!cf8;(Rgc|4yR%I(Tzwp}6$oQB*mg4%Yr}S+ zvb|lmwRYPn-D8S+zNSkpmF!_4>lmOEM}A)Dg>6n)%3Q0E3HRofLJWU7Tpg3<32j+V zV9gB5RiOS=lX`|%p0V4hR+=B~zQ$=NZVXEEnYMv)y81Dcsh?4%RAItI5+|x$_0iTL zl{hc=7Ci2D9)wSgft+*#(rV@sdV16zFQ~7Pa%&cPQCjka_wgOO5$v*K_IJjm0`@ch zl_#lC+~P2?35~B9T_YJ2w&(FcqJ2OZvIB#Dr)~bUbr2g|@Nx>(rPAHa&c0*7KIG4| zm2gr!!c6(<$bBy|3fecPEvCa-Mj}7ww^e-)srVkNzK0p#Ye(S?m5T2)ixwlotc`)) z8vfuMv$oqEiy?#i)~8=urb#?rkJg9G<~Tvo*wuE|3_yVEyTga)fqJxF|bJ zZ{Q!A9!@Gp3PQz>R_lU_p*_b4RaBWwe#Gc+df`o1Wy0GiI7h{E3|~1u!Mf3S>FofCcCKI#FsJZebMK%vNf9bDK|z(mkMJ(hQgT9N?{Bn zb>eQ<&hMuy4P@rx4V~Ywv<;yth3+K>(OWdIa>w<3yKp0r%?~}|pEYC}=*V<{rj?R5 zj-La5F>Uqn((lm5Mh&kKR*#{!67JQbE(falE|?2>MJ5L#c8YRVPu+xa)y&!XLwO?{y0F@#hw#I9CZ{Wn;$|$U_eK_kOs9yiR^e`k?9T;Uj zqqc6=!*q;uRUQh~MEx#W>OJvxdLg4wrDET3NgxWSTLktipi(og6!D|LLjjjx;dJwV60`hRtMUZ4QM(G zdVY(hU|S#c8;IY&SfS)Z>PuKuhyJlv&Sx4%`J%&;nl$FOR+U zIXE-XWJyfV#iP$Jj{entS0Aj6@@PQGP}AExabu&OA_R*VMNBi`1CMCz=&}UuGu^u$ z5yNjm80@j_Y&v`*W7U%3KRj{NMk+)~ZowWk%@cNrxcH$`3l65!Y86GFN99;l#E4>X zZh$<|Lu)g>+HS-F2!NybirN_LjX59VC?HV|0oG~CHOcY1@a9lSJBlbR9y<#QC_8;O zlTD_j7d(LHHqtLl`COl^h?A@7m67fVKVQE}#4oFWjKs~fbR#}w0pph{_F_9?>W>wz z{_eKcrma1oV&)1sy^~r86f*9Gn@L|`5mVMZj+DyI`Qq(ha!Qcmq^Tg1>8MEEbv&)N zK?Oiep>lWTRq@#olmtG+5F|!*cN`Q%^^O!Z1^x;>-M^SqyiI&`-%LtT&_0yq1576{<3VNQ`H?vsdosA+2> zkK-O6Y53cLe{;9Z%+<8|<5LR#9EvQDJ#L#Bh4!0L=YC(i zK!ujQqsN6YW2TM9YFklJX$cBsQPB`Y8?aNI%ZzdCj2WYA`6xeWK{qVuxGDc(y%ecj z1sQu{it>9ga7|fj_3_wDk3q+CKPbWCM1Mr1i8gE|I255;7Hj2JWpq8Tqa+x(FeH`C z$jz*dWY0cE!N-_N@zlPa(u){bCaT77S8a%}rQ5eDKh`c#jL}yWK`01{UC!2nyeu)Riy#Q=+y%38(>m7!s%%={qI-L+!kcp-UT@@3 z&x+QlZCp34>nmV!&WtjoZ5-+esf;;NORT0tJuksY+r<6_qa{sF(i97Oou)?43(H(- zSyPpko1C9lI6LpgYst}T>Im`jq>hk};+!9vU1;!v29WM?&KTNZ6zhM=!ZQW+bkV|2 zeB4fR8oPfnQf#JHcyMtN?pVC5BH5Y<`xLGkVL}n6`bDu9LVYaQ7U`&s(J!{c<34B` zX3~7zyh;XQKQ(tQF9^g)W{HrvH}C`JL)##u*l#>g+8Wq{J7Hhd2OEQ(xv-_z+)tqd z!v;-i<%PA4dEpySF!2KF^{NUcHqb^LX0A!W#5(25bAh;~7eCXm*iu;VIKI)<3~-La zr`~HS#~MVQe$WmICU_>+P%x3`qF~}Ewt@f06ii^-Z-s&hb&kJq^AQrD>wDlC$VxR6 zuhdmXdUwFmP%=>nD;FgbTk=+87^f?la1^}-pVN2LF>T5B-U0hG@10K1NtzB0G%)#R zG3HIHJh^~5K2vtw?4A`So2Q*e^ ziQj{39i^$_->i57!g7x+i$R6(J1W6LAQq9kKq8>Ylia z&b2yyeI4Bs@4=7KJ;A=Ip?l(0;7Z*S+#s#%G`L#H#dUN~+}R3|8oDP~qmlMM);%$o z$yL!k(O=U&(d&kEPxK@yTGkhL#CsLx6Hh>0`M6@N={P@6XNZK(W%@(Bsz?PX9t z@hT9d@`*WAKG8`jpZErDx&i@>7g`(NcfCxR4G<6la4u%@^Ppm{%{M$57ti!pZ3e6L&=`p`ip?QKS-MHonHj)@h zvXoq{d4f?D{VB~8D!S`wo-jNt=bR_hSU@$!H8fAKBGDB76c(}J*0oMpb*&TQ(FCcM z;%(%JmI-?c=&u9hNEaGctrNZAe~I#NZLJdx;m6QA(UkH3HLVl3K*My;XVlix$;)%Rw$Vb-fR6IdjDxRR}*ye(1rQ(Sk9DuNIV_a7& zo?w8giYIU+4C^2@DV|V7U8Q*98*Her!Zo{6yP*_Mutsu@$Hf@-^?b!#XLZFBCau8s zxB#USNnoe0dITc{rGuolsh|k>)X>GQri$Xt6pjzEBHiyfi@0NhMWh1W1vGrtB3c5b z03L!{)dgQ_`t}UK?eiB8w%zA=r=2LpFneEiUB}LG58|YZr~mFQ0*ej>qNG?G&ct%L z1uFyCQi+M9c$}aschbYh#LJ_>d0b$nhDg>}iI=yD9ec`%KNEx4U@ zudR_b)Yfum3oImz4@fH}UntWdOx4goivj<*F4ylt0Mg7%D1zbI% zshWi9xnbQs?Wdq>GRArDO)kSoDw4!rM}0KRN$k&AS5mS5vBJ?OOPV>mR;JKfOH@PI zSf%sElD&S>LIP(7jFn-feE7*06^Dr%_HL%SX=U%+KYL?!L zZ=5*LHA_Q>#_lB+fB)S6Q19ymL1Uc%)B>Zhk8v(>iD*H!h%&Ab5tgT)R1rnHL=@r@ zQLkzdwYw^!3l`5j>qO)cW_{CY#qbcN^PDz;&&J_3lyFfp5&Dznmo5l|lIuA)Ik0Fj z;5?KcH_#PcHvkIQ+9~-yQQ%?%BgetMEP5MsswfgqC zmG@zLV_&$ou!YrJEC8z#TI%eIwJc~i={vTu?N-f`muX7_EPuJ)myL=1k`G9?X^U5k z^BwS0sq~yrwJ3{Uz^DC^+k$qO{hep-@iCTpOb_iE34X}y%+3&Z!V+x z2B{#~=020$a1bMp;gOgrA9WcHJe1iJvwknW6YtLN=TT}qY3^u+H9aU?t_gxO_tEoc z43@*8O}{kFt!iqff`0H+@`kFwc=`vcpX!Pp>Rmu#trTY1bKkfB6f{3uu$d#e)KRz( zi9*XuNIQ{-ag?jd6@8~SWAs+{q>aNGUDfJ!{}>*hsJFw`5t~}D*~j0f$Hy0cb{xT* zH_TGU?u$vV-{;sv)8kOdV7yO&4b`^7&!OT&Ump75(2;uY+0I`)=O~3QDBOgL@5S#t z4rMn8g1_0`*`^@)omFRe032=^<&TRM@#c*;pNmJ)?>Z_R?>i1VzF<0&cKK@hh;Xe9 zREOE;;DCE`GS1lv-N|v|Fvf&V6Wr)k3#WsyLB&hw&UNOoLXCN>UJx78R!(Ha;GT4> zeMuafcgIu~?#AU@mTy`x>=(d(oSMu!Skq+I91fcDZ^A``@1ku{i@|7ape>avuk(G1 ziZ)$lZ}=1bt~$-%f)~_pnfg7Ve$T7lW9oOK`aOtW=g>s_Ja#w3JdSTQnY9$3`ear& zyyk7&0T-n$^)0*@lUYC3#oEV(pexn`rmaoU7l%{f<}>Q|9re3`zYm?nZ%WW-ru=pA zkNr9xmkPJ7h8^_n;n%cu4y-ZN1f4O|Xu5Tmsp@3YX2zvWHU+v)Hqn}sO(V$Cvf8Hm z>LVWPimUgoHq}IOLDNbYg#{YD8Xq(cXq+Jjicexhh;*stv~sEmyNR@^rY&%-vzgwD zx8l`a#8=Pa=PTabil4;$LS>KQAc~hWg!(Klz-x*fQ$hg_sFe0JGKYv@3|g2{5eZbB z(z19IY@l`wubda!s;f9vPJQWlJ;@TqU5t3!Rf(65jJJV`S8<@&UB$?E*BJR-{JpnE zcv+-1)?PNvYO$9=&8fW%YEJjVNh687Zi=_zC&eC|ZfodqNw-EDTl_SvHHP>WKU(o_ zE?$Or)7IMdvfj34DfV3Vp0=AXSkeQ6N5wPfxvYogdb{Sjz6?0YT;MfAx$4SIG3eLk zm^kLo@2Q+H%M_qqFwN9PyvqWCyIFBXtmZIbCdSZa}&i?`vu(#=*|w|8t)Dd8|l zt?gtIWa)y6!K{gtV|;nxDkf^mzl6F1yEN+QlPt8fuO}wLv6&y3iCoqY^ia(PuBpVE zR((KeGxRlk{l*Fp4YylFgj59d-NwN44i+Cn#A-t71n{RK)Q5<-v$iS!JlYIc6ubc+ zrmYn89v31E{5Bs%a6|Cd;oUlDalt;AMFpGii?uBpP)mDJv6pboRykXhOyp+<+w`u zDE^tVP3wuUDE=PrEe6c&p}4$EL3_?Syw_YJ@umUwa{a) zs?;df#TS_~s=|RrRK|~*P?sW+M=T$KH;?0v&@x9{dGV+Cu-$}OX{s$=lS)QXGBju( z^n)uYb?jSsX)Wv)+)?zhrp#2WL#dh^%1k#P1@IM9N|k)aVKgW+rI0e9!$VhQx*IVr zhovJF%1j@`i=OFnGfR@1QeqfQJTT;>s1>OY@vh2DSFx~AndvtmM=3L9D5cDF6JBDl zt?!Si|WnHGq93kvolLg*RCuYE@>zCXen zw0`5aI3AvKxkM;a0lzEDwzY*8uSMezm70bsrKX|fkCZgk-N0Hyv8ihMb!%%)(@X}% zdXmeLQ@VCjyQ*LWr^YPK zYW36}5m?e+Reai{dZl}10WYaDLQP3|dF;gW`?&xW{7{*eihbKgM2Sq;0O}p8c7;Ze z0Bqid$a$u9DQSS)YCO{dO1yCEP~$Z7xRk;oX6;_Z1#-->?FhaDRD~I^jl3yTqPW4w z=3jEF)+nW!wN`0_bBUVSU}1*NZR#{VE;lm_CT#e->J$7HDd9m)NN>*j)YKAr!>Ofi zT26b~+B;M#CC$?UwYVL-M>soIkNs==wu1;MY||a9&fo>Nv?fAJFy5+E#6}IwnmRsa zsPo-lkZTyc7ckeL2-RP1rjtgDmYj13W@9|I(ZjfcFLO7Rbj2zcK4eKdtwd`SNtKHR zU5cPB`m_>1#JnClLDo(>L07RX9{w>Q%D8ow*|%+ASSmE-i_>Eae5_Y?MjseN{Q81nq$s9W0&+4)s;NOHM4Y-++lFH(1ut-PJ1HigD)TQToKvQ*T+sQ*YoX z3ZUDY7I6>YKEQ{7ci^UN1H@1@9r&5e*6%(%Su=j5uZN2mhi_ypT zvE6ES3g}FSx^!EkxU};n-f?NamUzUaUBC^{rx1DV!WLdVc8o8%+4*G#JM8G`3FkL> zwVSzXf;$&A1fspQbJ-uv8y{4k^F29nj-8ljaQv)r&^Gk(qNfY$9+2Ml{(;gOsH0+Q z8SsJCH`3}Ic?~S=K3*7ZmNapWuEb&@UZH?U>7_ET&}O9koFN*9&h{1F;jhZPOLJ#S z-H&^PALsfRkf=|u)|+u5%o|fqA38j})zz6DITh9n!FV=`_X?{UhC!Qtxv;)ZABxB( zdE0v7%E}Q~xmOoq;=9>Z_xeJQ*TmDf+Sizz3IvaFTbs3|id)+QsVkf<3hP5fwG&Pv zYq0hDDDd5lTZ!j;Bawznk%*of7(~~kq=RAg3qbv*4IveAh=H3bc<|v^T0Q4C4wf+7 zpUFXfB5EAitzg8^bHSV8rNvYf#LBDZHmZ~48RFN0E-toncq*G(Y72d-$^K7RUx>h^ zq~q-iu=%17Fy!&eaZu%k9r?=cmaAD&3-fd(9=vxMCqWB*k2-Ta|ai9 zMj2NZR^M_T!eIyfN!0#{MLvoSOaf__S34Rm+@)yRmD6;O1sA1x%RQD_b*W1b*Hj}= z$yYnSuLYernj{>+^&PmmL(i{06dc^Qjz))E^>p38!lJ}XY?6*l1e;@dgmHI@>FkbJ z6di1YK!99qqW(H}r?a;84*dX7iYeC(5aP=pGk*g4W8qH>f9~Q>R#9Odq90;Ah|Sw~ zICf$4gw<5yfq81Ux)nwG4uQUeuT9n#j$J*z-1&pM)w{4+QKV-S)V7`UuzD?S7Ba;4 z+xW4&9Y-#HY2WP|fD3C!Iu7F)AKctRqHMqIEMXYLp;vs;;N$sP!9`b z*E3lnaJa+~j=NUX<)wbkiOLQ-SeirJZ^j&yAH8aGbC@Ya4wl^P_$Xi>PM^4sEvW|$ z*zcJh*-;cG+>FW|YBH(Ow!|MjXv|>!{VLX-JC8dg}Sm@)!iHHL@zA&tBZ5-6y>1na|6}F3GENPxG&e?VlUy4#{ zE64nicUm3ioCToGQ5(rL3AhsD+=o$@I&9*MBC2e zjx9fDU91o3Gf*$$o*Y(qEHiPqff5x|&~a;W+JHFcPtiyh+v70@H9F{oH5NxM`p$M& z`svEnkfNYk)9`Dn>+Fr}S*vXJ*ygOEPEK48W$l5kKsV=28{kG=!OqUlu#Yo0UgFm7-l&)ori0o)#U|+?4TO&B#qMWo;t=kI& z9ZKCXkbgCRiiye(pDzw9E=HV6grRH7r(gWJ!r+-7mK@~dqUQbQzm=#dFi|dv(H*V#r@C2kP^6HMR%p# z`44;{>&AgP+&g!av<&wgT-X5U_w}-!Q?*90$vzzXPxHhmjNEXZf;9>aw_)@$GNw2H zZ-~|gPRw_|c%o>qJ5+xyEkKL|;DR{r#%oNPryj>DEe=irCNfp1+Vpv?uwmg$PqL@G z%IxAV-~#2AW5zg}BqI{w`}I%*UmSf1U_f=Oh{~D*jJ=G*Q&eT1Ml+lIOs{s2MKj;F&CD(4$Z{m$x zE1`hK`RX_5FNHgm(zL?SxXe#l$MG6n7U75C=GfQveZ;{_ctd#fd%kZ#=`FvR7VkkW z=6a)Iy7w)-sjI-^pi{R=3~Dv>C&t3Sj4|@DsdFpVGW2^fU*NKaP$%7{afX1YG=WI7 zoy7r}d3AF=gU)4pI(B2pX%DIqND-`8*pW~H#7{&d7gQ{oB=;aV_;ML3J zAl*P=6j12#rMhp?IT-2M`_!`4b9Pe5VDFc(evN4(Z~(88u9qo zQW|#%oASfJNG9_lI_cb^+6N*^O-j0E_to<3aI$iR$HkFow%FKXeV|EsLMps zmHlqye-r1{$wpP?yc4gu3lARZPrw3MA(j#*?v8itQT-ZI!A^my;gJ1Q?#>@-Ta$4M z@?)?-=Ooh$FdUtm%rR#COk(GzHedv-a^qo@n*giK6bpVbV(>HTF8nOWg2PnU+P<%VY##O z#Yj-OL%V}~je4)RgZ$Bxpb&D0JIEvWT6qV#ok?hSkh|-5kOzE#OUMhPaS3^+gNntd zxJriWw>z^5z!}3Ezl6L=9M6))I!_$0tU++&4$_^7MP$E{mOP(Tj=Igqfm?B5HL=|J z$^j$YzPOFN9&aPpmal6&cDKVUgQ&cY9OG%Muc|W(xQ>AJ$M7f6!_0C^b06b;EgZ;d znn$gz;0E>o=kiq4V2CG<2l{A=4;M~iC8JL8xh|0^{T^{x3az-ax+u8xzLE7SEKU8D%`##&N-#4?}-M{O%7jL`qwx{1oTpxftDi8H|uir^) z9jsqUneBe@3&+m!>~g8|VjeMR9@CH&mT4`1vp_bf=5Z~BZ?_?WR-8h+f}`r%{Q{M% zxLkzg(rvwc`1P^X!MEqdQ&>ZdyLd`p#>JAXhqj=5%H!~OILUTPA^ZP*{$Jog85Br) z)p8Slfc5|jU?d;~Fb}X2unF)!;3S|Na1-vNX%FZPhyY9iWC4Dv>n4r?*5Q34;4Q!> zfHQzA0N>gO2j~YF1F!-X12zJ701g6<0e%2n05pI`tM-6EK!3n+z@30;fLVY%z=MEw zfHwg90Y?Bo0LlP$>$r(FfKGsZfC#`?KsI10;3>dsfR6!R1Ihq50e>?f5HJuh9B>!F z3djen2D}2;5BLqhXDMi_{_Jdt1Ngxf@y$x;GkFiY)Mi^Myqx^hBC>C-{H}1&U*4Gh z$(?*f3nHTV!f|(r5Tz*4Lt2H1Dfr8Q)o3wFM2Ie;kIQ>^(OV1?;jp3ma1kj&#Rw6m zY=(#-qMw+7zkUeM7=%dD|2hjZ($fCS%8oX3^*`bfExIZDZpw~fV_?T8L^s1kGB8U< z{FCvUt=xu-OfjpP-3a)y!rt%|2lp)4xQ4_)PfP{mz@ASO-qVq?@ty(Sd_oX1TcpB` zI40tK3iXhJFUg2M8=+`tgi90|E;bsz0$d`F0(>G~7?>)27&mb+($>rjd@~)!sHJVB zYotkkOo#C#B0d|^Ptrrs53#NM9tCXaBge%q9_c3`hGZApQSjyZ9Sxi_T*Ab`z3Mm9 zHqsN26s7~!?J915Gd|+Zc!(>*^FTts88iCjDB(!L)7c!2$IO?xctmt`x1^+Qc)=5c z><$9#0&y`OK!%7;oGTCq%xn>nJXu5~W{9{%t1UYT z4tOH6Q`Ot3X}0Vf-7Y>kDI;0`7-iGmqBAp;Yn)9t6Riv@5Kh3qfIk600`6icO4Ue6 zPdG|k4{^KbigGp#e=5E7oQUk?WD${`6PIiqlbDWhcpvQY9+IA(IYoKKkDI%PXDzSV z-gWBM^Qqs!(fcw47{&Rx283+#S-kDk4H z-_fUUzo7mD1_oO~28D)&M+_bk88viR^zaceu_NO~jUE#}cHEugCrq4_a985wDM`sG zQ>Ue-O;4YZk(o6!JI899HG9t7yYHDde?hJY&CCv;lWL90&YY6W+@As2n*!O$hLj|O zvLuu+<_}9$1|%yLK9W&Gu$*Tre`ZBWeZlo=%GWTIr#Sq%`q5nDP%8}=gKKbsEFn}h zN)~-w9a4bby+t6n-9s?0F7OiqY_z(Ab%+^|iC@+n#4j2cL;@GHq9#e%r6`PND8JJ{ zNei(oBVWI)3lg{jpTlRi#dgpZ=2I zK1I2+Br{DjQez!shD!#1=K^=8O1CWhF-9#!DqJ#<4`xt9Dz#W=z?LAj#lrJK1!Br$S{QyYgXdbRpl<_$jI;8EAl%7VM%c^{E=Hz zL8}=lWFahDAI7T1o(@x^mbQ#nbD0632KI)$8tHVeNT+7GVk}kjn{gZb4h6oW@XdT7 z?==^V!{in5>-ry&i|TX)R?uPKWbmyf3X-bv`*!pxjPk|YPE@5rqlcxdrZ~(><|wxY zE|vLrySSqwJ_C;%%fH!3tL7B1&O_JqdjEy=Sdv&q|4MqjD$>h>Olo;Q3vp#5PWD04 z!L_SPj!_mXIi|_s?V@Kzd^gUo1Ypiy!yKe*MVTdsj4w)}k&Bh78Re_H=v$FqP5GUP zTxEV~H6P1!rm7uSOD3aEWG$7fVqhNd(dg)2O^%2SV`4p^)h(>2C^I$H^{(+$$`A3o zI-VKeGHW?fK27mIQPo{q9Web5BwV^y9WK0<&fNGtzboc%6fDf{IV5b zFWBI%Rx^_`MjmPL1iIwUjmraL)nt%z!SnH;u&v9&H{V%{vvp!ir*Vd@hgQ35VJKadyr4XAOce7Iba=un`_ZDd zNvwv+UdLFNoG2798^Tz9#v*XkM2v;mi1sl3U@R}ewY4xUFrj8i9Q?r|Zh?6hOe(AJ zg?TIOi!GuROmCQGn5&%@(HiE)?<|mG!~>I^ODoK~VUC4a4l@QOhiri`qgB~p`^Ykr zqG%oiJJPMy3ZWtZe`b^zN;V}}>sbxM8%Hpejj0zA@&h$`{*T*3?>P z#x-4Wb2fel!Z-7#Y6{^9r}f=hBj&mo&$-6dPtn{Fp;@xhA+vlsX4ulx@ruo_UYG#~ zzdgK!m%FcLczAd%KD`1F4?UXu#Eh-&E$#>mjE}+QJF}TtCcN*Ob{8HY=48#m;|(9U zSjyWQhByBB`QHZ|Fkki85%q@lceUHqHbamz*Za#CSN~P@zfe^ExrrP5bB$qJ-+IRCs(g|YVEr9Pd~Ha+2@{r;l-E!wejUwUfr~L%huOkf8))!w!OW5 z$Ie~5-+6b>-hJ=A|H1wbKRR&m(8q^A`Si2Tk9=|T%VS?1KXLNZ*WaA}_Pg($#Xpps z`SGW-r9c02?)ToHYbxdkVLv)2IeWz9wB#w)$c&WC>>0`-UJElU zF~=G*#hN-RIVLm9mZjp+zO`sXG-lxvrzQ`|oD+|E{5Un!SbdHWQ3224Cow0)CkjGt7xu@RS7qocRSq zy1MwuPEJfRr(|c&fNvFCv~A6GhY(;i1UwlF6Pve~D4wXy$-t|E)#jPDy6m!88jCoVjjnrsEjQmy7GnMuj!%oHO8`~4jEl8XYPd(LoX!<>w9LIzB2w5J^L z6Fw&kf~Vzz#%aViV@4u)4sJ7PklLXu@}>jda;7CuPK0H8YDO~hGaWO)HN-J{TBU-EDGeMz`dQSsjdkl{BlAEAyWz!DDK6X2y)<46EV4YFf$J zGg33aeqaNZLs+`Zv}J;E$X6Fpx)#!-T!L%iW~W-GG3#=yiP_N`WRGks(9_$S5H-Ytc&V(@##<>$v$Fm~OnUIq@BP%^Q!KnKtB&Ft9Cs=#j-Zd*p zRet7Pm{+(1Yqj^*j2!l$acV$(qMOEdKy!-41AM1a8_l51Q@BU)P>$|^t+x6Ys z2VCF1R_Chj`(5ap&;|E}0Qea6VONmigYmuO_NwmH>7N)>)!j9I#@h{R?R<>*s)v7d zkcG|_?nkPne>~Ju;r64;dv$-S!z=y0;PSqsT6`fW>s~sj^}szRoz|r_1L`@@e+WKfxoN!$%icBG{Dup zIv+oLxT<^ge2sdfs(W?%$F9G=d-tcSx>u(!Yg1MC>gjjhTh)DEH97cspXM&`biw-z z9&UV9&jRinIf=RgdvJ_rCG5gZ8DCY+|L)cK_wChb=H|NGeV-fp>!DizXc$_fc+t`` zE}0$Dm_+Necrg=SuDy8lG_{_+*dRhxzs?v0U8`o#o+)GeCw|?-9#hu*(RfGNP#-(YADJ>Y%ySW{&YS zG<@Xn@L^~@lhU!dAlxm^nvMTR;2k$)SbRuKq;fdmJ|sCYOKqnRAE%>nYJOkaX z(CkzzI_&9jXrMXt5`8^}B`3~GzREsTqaqu5FlufVxpQx|d=C+aRs2y*}Bg7r#;fU~PzSjjE*x8brq~s8z zRq?LpsPr6tU&~&;!?U*cWgox56zyvdzf^|$F+NRdH3>nkf$jhG&(U0@(K9?mODH~0ux3kL<&>mtC1}t(T(JVR}OZxa5?ef zDDkMtK{Tr51><4~M%imv%P5+oGAqifct$JNG0E9#yqhrvbqM4G67c|I8I?L^x=!~_ z7w+km1=u%N(LXl_8?#2GBApz?8N7-6_3}@PcoFO|EHg1_SnA|#Y{mlBA1j#}nXF~< zqbhE_@`6OX;PQ=31!v;jBGPR+(-_$xTS^Lg)I!`xZn@MZo{%FQv&`%WjFN5HC}zp3 zTqI#<(u}Oc?Boi*$1}7G|HdR{r*dc!FXA+pq!B4h4)Xz|QID842zuRG=|&k7!e5gX zz19M0|6e{kdPBtU(9~v}bvF3wri;O~S2vgM>aTPs{P+1U2X2%Dl&9g}S>AlP+4eAo z;rGn|LzXy3=es9>YxlJP^#L5Ca~`%ffb+1NtEEXhnw*fN8|RJfJ#X1F+e9l z;YvE_KMz2h7wYCBn54xHpnE=m_+ai@t;9c}f3JZ_eAfY(-ZKFD+X^5}9|7q8Ie_kd zU<&y|AYcBokMA`fEnV|9pZ_dg|5LGFd+|%d;M$8X|5F(L=hL~S2rf%zwP^05);jB+KB2v=S+AK3pFGJeP{OhxPnjFwf9KkxYt5ST zRlf_bXjT^8+DV1egGb0fYf8fc}6$Kns8` zpbi>KH=QzXd<#I?H^2+v1e^pM0qg_32G{_25ReDR0!#pm0t^F$0r~@a0y+cy0WAQH z0X_gvK>63Ws~T_wuph7kK>wRyZUC$V(-$4K8Wji`)o z!@QRLwcP)#es`%(Wh9X1LMps)K;+ zwg~uR$kiWD_&3A-(T*67G{6_eDtR1pErtn0J(|DTDozJ~qEYuInNhW%^Tu-|tL`y}#`!B+IFTTC;aTa0mJ$p94od=+9L4Ctk3UB5&(lCqye3@W8tp zK#9gROuEybYdFSJ6Xe2P<_R}|2cR~<1ZX8G=e__l;E&|IXV0EE?~D_qadG1AyYE)G z88W_n`Ev2xbI*xQn>HyK|Ln8R#JAsmTOsFJoNn2OI&|aK+LZKrvhI;vQnriS?Ps^A zOwSa#$fA_(P{OypBmt5zJ@=D?Hp(>^n-}1+c7dHwe#rHtnbE{U;w{|NjJahoNV$?1Cn#abn`c ziDE%ggqS*Ysz^&q6EkMa5ZT!{7mE60{`~o3jV)L_fA;|K>VhC)pBgTfP7f6iW`>Bz zvMu7xh5f{fd6DALg_FhBm04oX{X@mUwbMn%x25R3ON#D$qzHaTieB$a(f=bUCVVJG z=qFMPJt{@)2`O>_qraA7{P$8!IVr{DGg2&ExKI=p7K#-sR)~imepo#6$RpzM#~&A~ zSFaZ9*RNOkyK&=2v3c`mRhPZ>)?4E6?u}y6&r)nImEzrZ-xcq@_n!Fh!wtIjrVfQ18#)yps+V6g`CQp!~oe{jF+)uuAC`W$`xX>d>Q+P4jJ{SXpHb}V$i;3 z2{B-~5W_ZN{t@A)mZGhc4aE|Ke;naoLiimB|1rX!b_w4e;Vm&j+?j>5Ov{B>wo!;@ z5q?*x5Qh-{2*Mvn_-_!t7~#(%`~{cr-P&VMW(Z_`Jod$66>;M-jLDzHzJ}c>gdaB) z@@K4Lr(-V5O|X}S^PsspHhO3{gt=9`2Z*j>m8u|nQGQ^F!c&ij`v5OeqemkmA_OQj{F34DXHbajlSI`^!=sJyaRKYSoaSJ+79ap@TvOg@h@qVVyd*^Ka9p{oo1@ zA%mhKBg4X?LW6@t!VK@uBAbfBLBM6O3xTR5}W}3Ug(Z7uuNJdt~ zpU|Xnqeepqs0acSm960p{KFVNBns}08?_v&<2I}lQ9$^F;E?FyQBmPh3C$TnGry)y zZ}#!=X)%mA(wz!AqLE5M^C}(^$OgKHhDS$6MMZ~4x2oa+?j1U*_ymmUfV+V&|rvblo1^KBYz-ZmU;~vj7SKL4i18>RXD@lc!u~k>>C{d zK1RAYlmB7L2kh_Y5gLS|;_9s8NB%~IK@cOud-bd4>=HjRIx?hR)zBy(RiEf8k)wW< zJ95iRdBG>qx!3{7)8Oy)=W-E8b&xgnXk&6_tzArhjQngwm{*RET) zZk=dvZrb^l75(96Z92AV*P&gvhQ6lT>f^h4>$V*_z;8p}R^0-+1&9`H zI(6*UvTnDA@X(-s{aahKZr8C}y}BK5)h*2Cj-9%Bd;4@mnA>h@P`|lf(@x#$d3)Eb zQ>&KGZ6;H5Pp{^kTGsQfON(y4t(w$!tK9~EyLD?>rxxSC+0VTZzUsBDTc=I{#sRI{ z-Qv*#t_ac+-$*~8MdJ=_1G;q!=m7kYey4x{|A2tj0gApBc+7ZOw^pAb*93h5wc!zc zWd&|9YkFvJ_@RG<6RiYJ9%Fm~xC`JW%=rCVk2^x6$F8<XN|HN}G>aUkJ z@vR4F(yCRf)-VbFfcACj)WHY{$5a%j(1jK_N~~?eFgT9Sf6GJu)CXX6b3+e#>kFXx zo1c90$#}FoZ=OAS_Pd{c`ssVLJzxL$HL@xb#fm`A)H<7l~k z`*!*L_uosjrxNonoS>2?PMnY!e@nW928l8FS5Bw17_^@H_~VbC*tv6O?w~<~dLSO= z6V-e)1vCT@7v^hS9r#Wj(~VniaO_kx#au;?va+(@@Q#M_hVgF(ejh*??8!LpxZ{rY z#1D8W{NI27eTg|z3H;=1uf3-5#vGFT?z`{g!Gi}S<`k4ahCv^J_NNi%$(LV#dH&X| zTj!(O7jC!PM`UGXg)LjQEC&5*;&vM#plQ>lJutU%=k2%OPTu*2g@tuwym zPNFZfqHWu@y}-j|Km726#GGygpAQ^3AiwzH3xy~0N8!%AIeGG={PN2$)i-G}0DT_y z4w*au^Upt*LGCUiPUmmG{U(3;<(G4xe){R_-+c4U38Zz2VL;~tC~v)h!!m~bv-qPw zC6QJI5Pt*6R|A+Q1`vPpil*_-Z-PMwP2yt!aFzxj&!qu|onihJ{CDr(y%hP_1~QRP zT6XQ)rD&jhV7^H*4=~T9b;GeC}H*f4y+wFv<$c|BXBf|F_?MdxgKhe=qdmm!ZCt$PYyW>m23*`AT}2 z7sQ?K%>U!Zk1OCic}{*4U&;b$A>QOaW%Q{tQigpdrR8H>NrEZ(JFsTZV;^XEN6Jp1 zq5U=~+q@y=vSU~qC@+8fMv#Xeg+Jz<$&@Me_YDJINTNbDfmws zkO#d#kn(oWknuUzJ8lx)CORnZu6bg} z6;1M=?rawrmi3J5Gv+kPC~5dg%1F=<4jMN8=<4H|??1!k(Q6RX?9!!6675VCAPoi> zbkvk51}(01T)uo+9(sM1Tt6>LJ~}g4{xj2}5WDj`DMx=JW$Z~Qqe;UTdU=M-^f$^g z>m-zC)=BMA4p^SMK%Q8puV9_61{xIp$nT|?yJ&-YJ)g9&KBQ^TK$CJ$xvox!Azzer z%F>Dbo8&XI`^&Yq0rH8Qfr}73G;U=;gU9>m<~v?NBGR z1`VxV)9O}4v#=Ts3ja23+Emp4Xye(=UzHy$zibbT{9t+Dw^2@rKk7ZXDdG1Q=nlLXyB8G`f~zk7>hc76mI_@4Muq;4Murpoz#6V_>LPPZX*rgzZp99N1&d< z^HELsqrO-2kFvIm{UMe)gARih<^kIS*E}(3p-KE%Pi|fqB44^ENInM|)`NyMRt^80 zvr^tw0vepSiV8HaJhM)ULY-ukXVPGlXVPGlXVys_-&FWttd2j+8QT~1vnqfz7*L%K zqpY~n!FSTYXKQX>`O3V0@};|jj@F*U}&4=P1skAptaCjZMb8lxNmSEYBe* z3#^m+piW}@Y}82|w&Pj{4gc!(QZwR@{{7Nky?V7lA0?l3uwJA|nIRqQ^Ux$Mv}0Rq z^vmeR_LhAHK5yjpm0K3{l`n&a7eT`Y(D2qHnezNu2+s{X#h`Nr@}v*jXV75uF*>}h z1+LD2))$8S_v_cMJ@diH@OW_ci=jXYr;@7h0Re~2_v{&z1PD7S%z*FeLj`Je%1f#sPruspL) zdIa?nAM1YyI54f6Tt zpO@^H8errH&FhsD%*)DyPbA8n_B-TT3qb?Q!mFU+UwV0FowUX_P_D`zC|70$%Lg+o z^8WM?=>QG)f`&z)VLoW!Q@xKd31tJ%RrL??hb$=hhg|2AmV58LSHAGV3yL0t2AbER zgEUdL7}j~{RkqG%N!ROF%;b%80fE+EG9wG}SLh4Jq)l4_0<(AKd2`A{A|WN zNBg@1`xv4!GBVyLt}Kr%0}B=`P&By8S9Myd=Lx@AC$KF1(ewE`FIDt0Se}dY@?0(4 zb^AZWpLsuI$Png(eD>LARo{z!8q5#KS+izU&~QCEu9qjohjr2>)=7UQzaIXTj5waTSSm#T7&DIZnuurE{-E#y7h2G&*V z3$Z`S@c*iA+Gp23#v^)pUXHTBrzT_#JIqy>(AOV@Z-sxCE?s(K zYflEQQz$_{TIIu2Pdz0^j2I!Yw@4Nh6-lfq$p;^NP~pSzJ^4)<*cPyzpj;6+h9M2C zPbr6N3(2E*9AWa~XNdm=`Tn|Dm3<791@?b7IwzXw|Ka!xbAN?c3SCI~fvm5< zxW5X|0D}&ijE_K>GU8_4`r)d{@~r|3+Gnkg!S?z2`Jr;_15@RfA8e5q ze*N_@^81G8AF!8F=I7_1!yYBMXwjly@4WL)nVz1m_>OUa>02Y;zl~E)519j zw!@Tr_K{dtI3KYc<4M}FkHmI@wAAo`1(%L9zy9p}5931FU5z=)6ZhP6&lTc{eWMCk zrVSc8b?PLscTMF3+YHJ)`#uI8#FzL}=1C{V1~ge7SVmYLj69)98D!tYXnQ#J=J*-% z@~7rMS+*$ukfk-)FZKz`DOSYgym|9fK9C01tC(AsW5pC~`sQ!Z?gY5qpd?h|7PMlEq zAa5o57Ti^=$^-ISLf(`Nu#F<0>7T%F(!hF@JZ1g=$}6wPmtJ~FwSoWo*S}Oa&Jlo5 zPSkA^(MHY#?z>=jACTs{$BnMvG$X$3|FHf?d0fVCmN%Njh562U0dlJP5?Ciubt}rc zYTsDbP`)X1#GmDW<&t?qIbj}fK8x4x>>mojsAC8F##GQ0K`Q($FV_c16I)4^- z(x~t^`v2f}K4~!OMS~WD2AbqI>n60_YMelsVq5FVU*gJd;?KM>`Vd^#q1;oJ$a9t< z)EO&*$6vv{0)JQeXC2|1A2sC(>Eaywgb5QQ_T?)1HhAu8(jR4svQB%p0mR){AHf)D z)!)Ef;mn{EPNGpR|zwGz~gv8g$SkPg%dPED)GCv|~Q7?qoS- zp0O_CS_0RgNDKLnH2z9GQ;BiaH-*0;|L7~UC!Yw{%MGm96%n|A^E>6Gp-agBR`G#Pt+3?^FO44Z72ILtp6wnY>(J>lE)l#lK0F9 z_63Z5;5X}h*0rq1Fs4xJ8ld^#jXUX3^6x4e)#cpyHp;E5Nm=JN{V*>m^W-yWq^v`Z zuAq4*mKYDJ02kt@mPXg26-Usf}_}h=nL*uf2_Uv*|TV4sCJ^Lii z=agzD-qiQM&-BpabJIzbKThhXZMCfm>_tz}Rjk%5)j)GxRxsMSWY0w%`ovrK9MdKZSX+H1vVP z;J-Vd4f-2rr(%tR>tvh@wP601Yu;RI{p6gK2QVv#^GJMtg8yqhEm4QBMVe)-KUqg| zyhI!b#u|p+=f8q_^&INl!>BjkV8mQA<$5F6xwyWG`7EN*Er5)y6i`jCp!JA@1(`3{c^qRPR!kMy^m{Un@U|>YkcP- zma9Cd^f?}6AAvv|2&~@;k^y~=QH_7tatsOt((RH2d?{a4+Q7- zx#nxgBiDPm&e$L3r&VRL726byUlY;K9YZ_}T$umt0}~gvKW{!VL(OS(&6#uZM*75I z5^&(UC)dxFJOT%Asd6x{Fze{7=OfYa@pMyMM z-}oc53p%1t`*bAdP*YZ z6~?&Y!L%voH2HA7jcX)aFXTGamWQ+caLw?C-*8j=39NYn2kz%#nc$i&AA^4OD{!xF zMs99y8vCFG0}sxdkQaP7zs|KLu5oa!jO$EX-{3kK*O<7r!8J0jFU^~x!9N$JO5&j8 z5$mqT+Bf5KO`mlDfqff-D;~s!`M>kNV9E8aSAYZOG&wiUH5SSv*SWa9!nH=V#-*n} zKPiGqsWM^6;{fmhPeuN-Z-#Yr7nh<2qTcjsp{mIiaoNPe9toF4Cr=4r;~zC1sH1 zkbQod#DhS75Qqo)#C*8kb9mRk)S4;R>hggD*GsECSJi(^-{Ej1KJmm8W4JcN{y6a< z&pEEOI!G zZ2wsQQx?b%$|BPyE__%fe){?o`Qz80p-fbhN0bT5BcGZQHsqh8YsJ#G&JU%ryLca1) zmMl4q&Pk=LRbj)xfdhMBzIQI^z&d8;`Vdl)4itnrs*bXvoLk5@@ z>jk5%qMazmy3AC_at``P)HTLEPk%I~YDHdw_sek!&mOMvaE=}a{w4E*>uYG2RXXes zknc>Nz&;uKXoiWl>NoK79>nz|)+>HQ+8he}(WB&#Wsq^PZ%2M}E|)UMxpb~;uzV0t zWA2K1z zpw^gKE{Go=^1+znWq+A#D(ts|hR2cUjiycfRQiTIldlBgL121pkDwz#)eYRMO4=!N z%rEkqbhA#z+{@E{GHsPU(?MOM>i?SXF#5nab0BfvQOy;zU&uKp%H!WiTcuBWjrNza zM0yz~fps3s9LqN8q>OR@4)n@=m!U!Cu+{AV5zSogB-V?IMC1m*8X z%!d^s4$hza)rV(IeE%Y_eEm`Vc1^s>Tj9*ETg7?ZR(aqBzzra70O-#M(+WWd!LTzR z7w-g_SA!0gysOUbn#Hvq?A2o2H9nBX&?ldKaue2QE})M33Hw6+@$}PASE+Zf25=T} zWIp%YbIKlmJlC#W8;SYsw_kkmMU|gM8^(M_o&K3?Vq8zd{%6j!UPc@zA%Evt4mmca zyuO4nNF4fg+}9Y4vDIT32jbak#6iE5Y4+ia{)|zkSeGSW+{7^x=MX+dx27ldb>cDl z$AaqzOp9fW^%8;d%CLMAF+AZIc&pYWQ+E2#uQ0c;ZelqiuIxKdwhz9wPOiw*`i4{V z@f*jF9KUj`z_Cgo#!8O>FRrz6OitV>|4jGU1(B+ca}Hy$$AB~A;8>hvFV019+{bZe zAB;OWN6kJJ@n*fnhhrFyp5!B>V2{w{zUUvD5tI z!77co6H;!#xEANUWo~Y++9SesHRdJd#o)j4jGu!$H>!UBe2jhchs16s|IjX|dW&mv z+&{puhRnUZV4(crHEOn$Qw9%olnUybz_<%ab(`&`Tq)~Bwx@SSbB5tb(X8~IP(8U3ykXeXII z+arz>7&q%>wEelR;aN`;Z^lDjz+IImw%MFdVpxu|*>+V zEinAhKfy%5ZkWh4n{huYDobiya}&@=tiGsk%^hyE^H$o{Jm98%QP-L$G#c^CtTe6F z(tY9!e!O&_xRn=maBa~)F()T^#^m(5<~cLcGjayBv1MoU%b7AQc}8MRml>&3vNLls zQ>Bj;c808zGPrKq90~Ks_!Yl7xn2?#!LJGjs2q zn@D&XAX=~i*5by*o4C{O0?9-`%}?zF(0y5WE9K5kNb{j?+?5oEi}6 zc3VMzWuRMc=D!?`TceBY zWg5YdOwe^Jb(e-7?YO+agz_riY_C7gU0IS|Pyj+3c3e?jX}}w&tkf%G|Ge{+@vr*C zzs+u?{>)Y@^~$25yOsJAo+>LnD?KA)!sKz;**`w%dHuh`BXsuB0dW?I($NewA3cIL zqSw&@v`UM_!*Kx);2OLNx8S$%1$-U3kuWlvOea<35zn=$YObb@tLF)nQQR^wI!#ZT0u#)T)o7$$m&Ti?pq0Qc7 z?*~i1vcI#x2Wu|bebqn}qlT+wr71(DtBGow%2P$EO5Lq$!MbNuvw8{YTZOJ@Y=fo( zJ%`>v9cZ*xq}6Flw0E?RwO%+H55tL=x%&2 z&vZ=tvuKvg#<6AWP4+%(HwN$_JcoO^kM|boB3rb{kL6&~ZH_d#nQq={-fKQ=HkfP7 zP3BJXxOuZR!z!~5TIa0M_6GZXd#t)$dDR@XSUs(tQ?ICf>bN?qE~-9GKWC_u4tU<} zEOI_|LKm0D19y%@e)JZ4NZX{P;t6;LUV?Yvo%jH5$DQ~%K8r8nK4cJyC2lf;V8Y25 zGKEYdIi!|6LAHqYuPy$?;IgwCb`x`l41AJK#K5O~cGw2NM%;cNh` zbO;;9M#6<5lclk-Y$D5I)7VUw%Y5v9_6N3vEoDvY&uk6bz+Pc(Y&ZLWwS%W!V+=Ju zHa<5xjT6Ro`~m(bf0nQ2>-i48oA2Ytc^AJ{3=u>qF;?6mJfcjj5bMMN@QJhHpCU=V zD?gJbr2&38-3*%5=6za+&g+*aVDYwV5oJ9d;Bs>Y~0 zVds~tz3K}!(7_IO@}1ev9A~Yw$=TstaG>OtD+qoNj;=$K(Qi-zsz%SC6=*m51bvQ< zp*HP^c19b5i}8yPf!pvt{B1X?ZjwOK$W$_e6hI_CO+FzhfZ-y#nQmfHJcb_-mqb4^ z&fIK%YnI#bDn)&%zE{N#l-KJn;J~4oh^C@b2y2DfLahT=kvcL#KdMJkMC<8t`YPSa z-xSl$MzamZ*lk0Hipv@kH=`sp3TcQUfo$N6D)f8wC$trvKr!0O_)*eC1_Bq%)R*Zm z=r8H5`d(mw1Uii_rj2wX?CC&eut7$wF`Lil8~7G}kw=I@Vz@{V$K@s2*BoFDGGomJ z;HN9hJ?2nrghefB&9rhM7GJPhtnJoLYoGO*)z=PS%#w6p-Zr_OF)AfiBI7!V9|V1252uL4P+^4BwNWhWC$>5yq*ZrfZ*?X zT(|TGARg-V27Rf%gPx-<7S0B13mKWB#lhqbDW&cP7gO)ggkGz>Lszr&Nsz2tFn zfSe|aVc(DHMc_&6=wW)2u40pn*~VkWY2YWqZ#8eRKCpkOZczmxE?fuo3ZWT`Hh_=6 zieJauA=m80d+-$EA(dna?B7{Zs~^%MXdE3)y>u--OP^({Sr%lCIU--qm&@ey_L0z7 z+q!ENyGEcA^f=mtns7flkPfCX^m=*|9ZnOdNhi}Bx`6(b?xWw)NOmVM$1?UR`-Vju zH-RtAGzyJoqs92ah~e=(lm8C*qLptJTg4uERL(b_HTRo`%;&8&)+^wN`>kl(ZKpy` zIBeU%owbmCn!!&(g^&@LlYmmtL#PGy)^=;huudeojr^9}Lqa+7J$itCO1}VpgW47y zm-TG3HIJ4H5m~GSwYl1AZJoAJZ2`1*slTaq^|d;xPN@s3x6|K=c5ZOuon%LMEGNUs zbf!9<(0-h}TzKS^>QZ&iN~g`~gm=Tb@r^)UBKUIB$t#DS5%lQ8vNdcU^Jd6k_8gF;KCAVB14Q9 znIcO}g{cpqwkKWsO`Q>trXybOi89qM2!Cn;tV4&MAt4%{%O`?N0ls9ijTG zC^(ylRW~TNidTt{!@06ln#xe)Ri?^PQ&qOAP(d|URjV4cK-H;*s$MmOysqcggFp`g zJqYw5(1XDL9RkCL6-!URU*PeUS9!|k=Ldad-olbRPoThGQUQAR74w3{zVa0BTyNxn z$gEp?^^WddV$q9>Cso~lfBNU}Ds&HB$H>Vk%PT2|3cD5ahPy|N9OVXGT98VW?#f`% zC?0wzvZSoS=MTF5-k+WY<^>`ni~PPa_g#7ZyaHcJNVwKFCy-K5T2hhk%kvkyf9z8M zYQx@~K(NqP8H|i9DRSrJl!HKOPLA8Q-3dRhiKYVB#F7+7nt`s9DRh^QqjN%2E<;1J!PE1My9Kx=N?ym6$(y#vk fiEfDCp(b>)bTuwnKdP04!C9GrL!oaGF4w;RS>7&D diff --git a/libs/win/bin/xmouse.exe b/libs/win/bin/xmouse.exe index 4b7c018e2a07a0621643ee198cff91e8e893fb77..bd19cbb251d0b63f10aa94fe0da2fd3d853cb6f3 100644 GIT binary patch literal 107883 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 orig_wd = os.getcwd() + >>> os.chdir('c:\\windows') # so we know what the working directory is + + >>> findpath('d:\\') + 'd:\\' + + >>> findpath('d:\\', 'c:\\windows') + 'd:\\' + + >>> findpath('\\bar', 'd:\\') + 'd:\\bar' + + >>> findpath('\\bar', 'd:\\foo') # fails with '\\bar' + 'd:\\bar' + + >>> findpath('bar', 'd:\\foo') + 'd:\\foo\\bar' + + >>> findpath('bar\\baz', 'd:\\foo') + 'd:\\foo\\bar\\baz' + + >>> findpath('\\baz', 'd:\\foo\\bar') # fails with '\\baz' + 'd:\\baz' + + Since we're on the C drive, findpath may be allowed to return + relative paths for targets on the same drive. I use abspath to + confirm that the ultimate target is what we expect. + >>> os.path.abspath(findpath('\\bar')) + 'c:\\bar' + + >>> os.path.abspath(findpath('bar')) + 'c:\\windows\\bar' + + >>> findpath('..', 'd:\\foo\\bar') + 'd:\\foo' + + >>> findpath('..\\bar', 'd:\\foo') + 'd:\\bar' + + The parent of the root directory is the root directory. + >>> findpath('..', 'd:\\') + 'd:\\' + + restore the original working directory + >>> os.chdir(orig_wd) + """ + return os.path.normpath(os.path.join(start, target)) + + +def main(): + import sys + + if sys.argv[1:]: + print(findpath(*sys.argv[1:])) + else: + import doctest + + doctest.testmod() + + +if __name__ == '__main__': + main() diff --git a/libs/win/bugs/multi_os_libc.py b/libs/win/bugs/multi_os_libc.py new file mode 100644 index 00000000..b1443122 --- /dev/null +++ b/libs/win/bugs/multi_os_libc.py @@ -0,0 +1,21 @@ +from ctypes import CDLL, c_char_p + + +def get_libc(): + libnames = ('msvcrt', 'libc.so.6') + for libname in libnames: + try: + return CDLL(libname) + except WindowsError: + pass + except OSError: + pass + raise RuntimeError("Unable to find a suitable libc (tried %s)" % libnames) + + +getenv = get_libc().getenv +getenv.restype = c_char_p + +# call into your linked module here + +print('new value is', getenv('FOO')) diff --git a/libs/win/bugs/vista-symlink-islink-bug.py b/libs/win/bugs/vista-symlink-islink-bug.py new file mode 100644 index 00000000..a8e8f010 --- /dev/null +++ b/libs/win/bugs/vista-symlink-islink-bug.py @@ -0,0 +1,29 @@ +import os +import sys + +try: + from jaraco.windows.filesystem import symlink +except ImportError: + # a dirty reimplementation of symlink from jaraco.windows + from ctypes import windll + from ctypes.wintypes import LPWSTR, DWORD, BOOLEAN + + CreateSymbolicLink = windll.kernel32.CreateSymbolicLinkW + CreateSymbolicLink.argtypes = (LPWSTR, LPWSTR, DWORD) + CreateSymbolicLink.restype = BOOLEAN + + def symlink(link, target, target_is_directory=False): + """ + An implementation of os.symlink for Windows (Vista and greater) + """ + target_is_directory = target_is_directory or os.path.isdir(target) + CreateSymbolicLink(link, target, target_is_directory) + + +assert sys.platform in ('win32',) +os.makedirs(r'.\foo') +assert os.path.isdir(r'.\foo') + +symlink(r'.\foo_sym', r'.\foo') +assert os.path.isdir(r'.\foo_sym') +assert os.path.islink(r'.\foo_sym') # fails diff --git a/libs/win/bugs/wnetaddconnection2-error-on-64-bit.py b/libs/win/bugs/wnetaddconnection2-error-on-64-bit.py new file mode 100644 index 00000000..e7c7a756 --- /dev/null +++ b/libs/win/bugs/wnetaddconnection2-error-on-64-bit.py @@ -0,0 +1,20 @@ +# reported at http://social.msdn.microsoft.com/Forums/en-US/wsk/thread/f43c2faf-3df3-4f11-9f5e-1a9101753f93 +from win32wnet import WNetAddConnection2, NETRESOURCE + +resource = NETRESOURCE() +resource.lpRemoteName = r'\\aoshi\users' +username = 'jaraco' +res = WNetAddConnection2(resource, UserName=username) +print('first result is', res) +res = WNetAddConnection2(resource, UserName=username) +print('second result is', res) + +""" +Output is: + +first result is None +Traceback (most recent call last): + File ".\wnetaddconnection2-error-on-64-bit.py", line 7, in + res = WNetAddConnection2(resource, UserName=username) +pywintypes.error: (1219, 'WNetAddConnection2', 'Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.') +""" diff --git a/libs/win/importlib_metadata/__init__.py b/libs/win/importlib_metadata/__init__.py deleted file mode 100644 index f594c6f7..00000000 --- a/libs/win/importlib_metadata/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -from .api import distribution, Distribution, PackageNotFoundError # noqa: F401 -from .api import metadata, entry_points, resolve, version, read_text - -# Import for installation side-effects. -from . import _hooks # noqa: F401 - - -__all__ = [ - 'metadata', - 'entry_points', - 'resolve', - 'version', - 'read_text', - ] - - -__version__ = version(__name__) diff --git a/libs/win/importlib_metadata/_hooks.py b/libs/win/importlib_metadata/_hooks.py deleted file mode 100644 index 1fd62698..00000000 --- a/libs/win/importlib_metadata/_hooks.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import unicode_literals, absolute_import - -import re -import sys -import itertools - -from .api import Distribution -from zipfile import ZipFile - -if sys.version_info >= (3,): # pragma: nocover - from contextlib import suppress - from pathlib import Path -else: # pragma: nocover - from contextlib2 import suppress # noqa - from itertools import imap as map # type: ignore - from pathlib2 import Path - - FileNotFoundError = IOError, OSError - __metaclass__ = type - - -def install(cls): - """Class decorator for installation on sys.meta_path.""" - sys.meta_path.append(cls) - return cls - - -class NullFinder: - @staticmethod - def find_spec(*args, **kwargs): - return None - - # In Python 2, the import system requires finders - # to have a find_module() method, but this usage - # is deprecated in Python 3 in favor of find_spec(). - # For the purposes of this finder (i.e. being present - # on sys.meta_path but having no other import - # system functionality), the two methods are identical. - find_module = find_spec - - -@install -class MetadataPathFinder(NullFinder): - """A degenerate finder for distribution packages on the file system. - - This finder supplies only a find_distribution() method for versions - of Python that do not have a PathFinder find_distribution(). - """ - search_template = r'{name}(-.*)?\.(dist|egg)-info' - - @classmethod - def find_distribution(cls, name): - paths = cls._search_paths(name) - dists = map(PathDistribution, paths) - return next(dists, None) - - @classmethod - def _search_paths(cls, name): - """ - Find metadata directories in sys.path heuristically. - """ - return itertools.chain.from_iterable( - cls._search_path(path, name) - for path in map(Path, sys.path) - ) - - @classmethod - def _search_path(cls, root, name): - if not root.is_dir(): - return () - normalized = name.replace('-', '_') - return ( - item - for item in root.iterdir() - if item.is_dir() - and re.match( - cls.search_template.format(name=normalized), - str(item.name), - flags=re.IGNORECASE, - ) - ) - - -class PathDistribution(Distribution): - def __init__(self, path): - """Construct a distribution from a path to the metadata directory.""" - self._path = path - - def read_text(self, filename): - with suppress(FileNotFoundError): - with self._path.joinpath(filename).open(encoding='utf-8') as fp: - return fp.read() - return None - read_text.__doc__ = Distribution.read_text.__doc__ - - -@install -class WheelMetadataFinder(NullFinder): - """A degenerate finder for distribution packages in wheels. - - This finder supplies only a find_distribution() method for versions - of Python that do not have a PathFinder find_distribution(). - """ - search_template = r'{name}(-.*)?\.whl' - - @classmethod - def find_distribution(cls, name): - paths = cls._search_paths(name) - dists = map(WheelDistribution, paths) - return next(dists, None) - - @classmethod - def _search_paths(cls, name): - return ( - item - for item in map(Path, sys.path) - if re.match( - cls.search_template.format(name=name), - str(item.name), - flags=re.IGNORECASE, - ) - ) - - -class WheelDistribution(Distribution): - def __init__(self, archive): - self._archive = archive - name, version = archive.name.split('-')[0:2] - self._dist_info = '{}-{}.dist-info'.format(name, version) - - def read_text(self, filename): - with ZipFile(_path_to_filename(self._archive)) as zf: - with suppress(KeyError): - as_bytes = zf.read('{}/{}'.format(self._dist_info, filename)) - return as_bytes.decode('utf-8') - return None - read_text.__doc__ = Distribution.read_text.__doc__ - - -def _path_to_filename(path): # pragma: nocover - """ - On non-compliant systems, ensure a path-like object is - a string. - """ - try: - return path.__fspath__() - except AttributeError: - return str(path) diff --git a/libs/win/importlib_metadata/api.py b/libs/win/importlib_metadata/api.py deleted file mode 100644 index 41942a39..00000000 --- a/libs/win/importlib_metadata/api.py +++ /dev/null @@ -1,146 +0,0 @@ -import io -import abc -import sys -import email - -from importlib import import_module - -if sys.version_info > (3,): # pragma: nocover - from configparser import ConfigParser -else: # pragma: nocover - from ConfigParser import SafeConfigParser as ConfigParser - -try: - BaseClass = ModuleNotFoundError -except NameError: # pragma: nocover - BaseClass = ImportError # type: ignore - - -__metaclass__ = type - - -class PackageNotFoundError(BaseClass): - """The package was not found.""" - - -class Distribution: - """A Python distribution package.""" - - @abc.abstractmethod - def read_text(self, filename): - """Attempt to load metadata file given by the name. - - :param filename: The name of the file in the distribution info. - :return: The text if found, otherwise None. - """ - - @classmethod - def from_name(cls, name): - """Return the Distribution for the given package name. - - :param name: The name of the distribution package to search for. - :return: The Distribution instance (or subclass thereof) for the named - package, if found. - :raises PackageNotFoundError: When the named package's distribution - metadata cannot be found. - """ - for resolver in cls._discover_resolvers(): - resolved = resolver(name) - if resolved is not None: - return resolved - else: - raise PackageNotFoundError(name) - - @staticmethod - def _discover_resolvers(): - """Search the meta_path for resolvers.""" - declared = ( - getattr(finder, 'find_distribution', None) - for finder in sys.meta_path - ) - return filter(None, declared) - - @property - def metadata(self): - """Return the parsed metadata for this Distribution. - - The returned object will have keys that name the various bits of - metadata. See PEP 566 for details. - """ - return email.message_from_string( - self.read_text('METADATA') or self.read_text('PKG-INFO') - ) - - @property - def version(self): - """Return the 'Version' metadata for the distribution package.""" - return self.metadata['Version'] - - -def distribution(package): - """Get the ``Distribution`` instance for the given package. - - :param package: The name of the package as a string. - :return: A ``Distribution`` instance (or subclass thereof). - """ - return Distribution.from_name(package) - - -def metadata(package): - """Get the metadata for the package. - - :param package: The name of the distribution package to query. - :return: An email.Message containing the parsed metadata. - """ - return Distribution.from_name(package).metadata - - -def version(package): - """Get the version string for the named package. - - :param package: The name of the distribution package to query. - :return: The version string for the package as defined in the package's - "Version" metadata key. - """ - return distribution(package).version - - -def entry_points(name): - """Return the entry points for the named distribution package. - - :param name: The name of the distribution package to query. - :return: A ConfigParser instance where the sections and keys are taken - from the entry_points.txt ini-style contents. - """ - as_string = read_text(name, 'entry_points.txt') - # 2018-09-10(barry): Should we provide any options here, or let the caller - # send options to the underlying ConfigParser? For now, YAGNI. - config = ConfigParser() - try: - config.read_string(as_string) - except AttributeError: # pragma: nocover - # Python 2 has no read_string - config.readfp(io.StringIO(as_string)) - return config - - -def resolve(entry_point): - """Resolve an entry point string into the named callable. - - :param entry_point: An entry point string of the form - `path.to.module:callable`. - :return: The actual callable object `path.to.module.callable` - :raises ValueError: When `entry_point` doesn't have the proper format. - """ - path, colon, name = entry_point.rpartition(':') - if colon != ':': - raise ValueError('Not an entry point: {}'.format(entry_point)) - module = import_module(path) - return getattr(module, name) - - -def read_text(package, filename): - """ - Read the text of the file in the distribution info directory. - """ - return distribution(package).read_text(filename) diff --git a/libs/win/importlib_metadata/docs/changelog.rst b/libs/win/importlib_metadata/docs/changelog.rst deleted file mode 100644 index f8f1fedc..00000000 --- a/libs/win/importlib_metadata/docs/changelog.rst +++ /dev/null @@ -1,57 +0,0 @@ -========================= - importlib_metadata NEWS -========================= - -0.7 (2018-11-27) -================ -* Fixed issue where packages with dashes in their names would - not be discovered. Closes #21. -* Distribution lookup is now case-insensitive. Closes #20. -* Wheel distributions can no longer be discovered by their module - name. Like Path distributions, they must be indicated by their - distribution package name. - -0.6 (2018-10-07) -================ -* Removed ``importlib_metadata.distribution`` function. Now - the public interface is primarily the utility functions exposed - in ``importlib_metadata.__all__``. Closes #14. -* Added two new utility functions ``read_text`` and - ``metadata``. - -0.5 (2018-09-18) -================ -* Updated README and removed details about Distribution - class, now considered private. Closes #15. -* Added test suite support for Python 3.4+. -* Fixed SyntaxErrors on Python 3.4 and 3.5. !12 -* Fixed errors on Windows joining Path elements. !15 - -0.4 (2018-09-14) -================ -* Housekeeping. - -0.3 (2018-09-14) -================ -* Added usage documentation. Closes #8 -* Add support for getting metadata from wheels on ``sys.path``. Closes #9 - -0.2 (2018-09-11) -================ -* Added ``importlib_metadata.entry_points()``. Closes #1 -* Added ``importlib_metadata.resolve()``. Closes #12 -* Add support for Python 2.7. Closes #4 - -0.1 (2018-09-10) -================ -* Initial release. - - -.. - Local Variables: - mode: change-log-mode - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 78 - coding: utf-8 - End: diff --git a/libs/win/importlib_metadata/docs/conf.py b/libs/win/importlib_metadata/docs/conf.py deleted file mode 100644 index c87fc4f2..00000000 --- a/libs/win/importlib_metadata/docs/conf.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# flake8: noqa -# -# importlib_metadata documentation build configuration file, created by -# sphinx-quickstart on Thu Nov 30 10:21:00 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.viewcode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'importlib_metadata' -copyright = '2017-2018, Jason Coombs, Barry Warsaw' -author = 'Jason Coombs, Barry Warsaw' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.1' -# The full version, including alpha/beta/rc tags. -release = '0.1' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -html_sidebars = { - '**': [ - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - ] -} - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'importlib_metadatadoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'importlib_metadata.tex', 'importlib\\_metadata Documentation', - 'Brett Cannon, Barry Warsaw', 'manual'), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'importlib_metadata', 'importlib_metadata Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'importlib_metadata', 'importlib_metadata Documentation', - author, 'importlib_metadata', 'One line description of project.', - 'Miscellaneous'), -] - - - - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - } diff --git a/libs/win/importlib_metadata/docs/index.rst b/libs/win/importlib_metadata/docs/index.rst deleted file mode 100644 index 21da1ed6..00000000 --- a/libs/win/importlib_metadata/docs/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -=============================== - Welcome to importlib_metadata -=============================== - -``importlib_metadata`` is a library which provides an API for accessing an -installed package's `metadata`_, such as its entry points or its top-level -name. This functionality intends to replace most uses of ``pkg_resources`` -`entry point API`_ and `metadata API`_. Along with ``importlib.resources`` in -`Python 3.7 and newer`_ (backported as `importlib_resources`_ for older -versions of Python), this can eliminate the need to use the older and less -efficient ``pkg_resources`` package. - -``importlib_metadata`` is a backport of Python 3.8's standard library -`importlib.metadata`_ module for Python 2.7, and 3.4 through 3.7. Users of -Python 3.8 and beyond are encouraged to use the standard library module, and -in fact for these versions, ``importlib_metadata`` just shadows that module. -Developers looking for detailed API descriptions should refer to the Python -3.8 standard library documentation. - -The documentation here includes a general :ref:`usage ` guide. - - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - using.rst - changelog.rst - - -Project details -=============== - - * Project home: https://gitlab.com/python-devs/importlib_metadata - * Report bugs at: https://gitlab.com/python-devs/importlib_metadata/issues - * Code hosting: https://gitlab.com/python-devs/importlib_metadata.git - * Documentation: http://importlib_metadata.readthedocs.io/ - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - -.. _`metadata`: https://www.python.org/dev/peps/pep-0566/ -.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points -.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api -.. _`Python 3.7 and newer`: https://docs.python.org/3/library/importlib.html#module-importlib.resources -.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html -.. _`importlib.metadata`: TBD diff --git a/libs/win/importlib_metadata/docs/using.rst b/libs/win/importlib_metadata/docs/using.rst deleted file mode 100644 index 2af6c822..00000000 --- a/libs/win/importlib_metadata/docs/using.rst +++ /dev/null @@ -1,133 +0,0 @@ -.. _using: - -========================== - Using importlib_metadata -========================== - -``importlib_metadata`` is a library that provides for access to installed -package metadata. Built in part on Python's import system, this library -intends to replace similar functionality in ``pkg_resources`` `entry point -API`_ and `metadata API`_. Along with ``importlib.resources`` in `Python 3.7 -and newer`_ (backported as `importlib_resources`_ for older versions of -Python), this can eliminate the need to use the older and less efficient -``pkg_resources`` package. - -By "installed package" we generally mean a third party package installed into -Python's ``site-packages`` directory via tools such as ``pip``. Specifically, -it means a package with either a discoverable ``dist-info`` or ``egg-info`` -directory, and metadata defined by `PEP 566`_ or its older specifications. -By default, package metadata can live on the file system or in wheels on -``sys.path``. Through an extension mechanism, the metadata can live almost -anywhere. - - -Overview -======== - -Let's say you wanted to get the version string for a package you've installed -using ``pip``. We start by creating a virtual environment and installing -something into it:: - - $ python3 -m venv example - $ source example/bin/activate - (example) $ pip install importlib_metadata - (example) $ pip install wheel - -You can get the version string for ``wheel`` by running the following:: - - (example) $ python - >>> from importlib_metadata import version - >>> version('wheel') - '0.31.1' - -You can also get the set of entry points for the ``wheel`` package. Since the -``entry_points.txt`` file is an ``.ini``-style, the ``entry_points()`` -function returns a `ConfigParser instance`_. To get the list of command line -entry points, extract the ``console_scripts`` section:: - - >>> cp = entry_points('wheel') - >>> cp.options('console_scripts') - ['wheel'] - -You can also get the callable that the entry point is mapped to:: - - >>> cp.get('console_scripts', 'wheel') - 'wheel.tool:main' - -Even more conveniently, you can resolve this entry point to the actual -callable:: - - >>> from importlib_metadata import resolve - >>> ep = cp.get('console_scripts', 'wheel') - >>> resolve(ep) - - - -Distributions -============= - -While the above API is the most common and convenient usage, you can get all -of that information from the ``Distribution`` class. A ``Distribution`` is an -abstract object that represents the metadata for a Python package. You can -get the ``Distribution`` instance:: - - >>> from importlib_metadata import distribution - >>> dist = distribution('wheel') - -Thus, an alternative way to get the version number is through the -``Distribution`` instance:: - - >>> dist.version - '0.31.1' - -There are all kinds of additional metadata available on the ``Distribution`` -instance:: - - >>> d.metadata['Requires-Python'] - '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> d.metadata['License'] - 'MIT' - -The full set of available metadata is not described here. See PEP 566 for -additional details. - - -Extending the search algorithm -============================== - -Because package metadata is not available through ``sys.path`` searches, or -package loaders directly, the metadata for a package is found through import -system `finders`_. To find a distribution package's metadata, -``importlib_metadata`` queries the list of `meta path finders`_ on -`sys.meta_path`_. - -By default ``importlib_metadata`` installs a finder for packages found on the -file system. This finder doesn't actually find any *packages*, but it cany -find the package's metadata. - -The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the -interface expected of finders by Python's import system. -``importlib_metadata`` extends this protocol by looking for an optional -``find_distribution()`` ``@classmethod`` on the finders from -``sys.meta_path``. If the finder has this method, it takes a single argument -which is the name of the distribution package to find. The method returns -``None`` if it cannot find the distribution package, otherwise it returns an -instance of the ``Distribution`` abstract class. - -What this means in practice is that to support finding distribution package -metadata in locations other than the file system, you should derive from -``Distribution`` and implement the ``load_metadata()`` method. This takes a -single argument which is the name of the package whose metadata is being -found. This instance of the ``Distribution`` base abstract class is what your -finder's ``find_distribution()`` method should return. - - -.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points -.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api -.. _`Python 3.7 and newer`: https://docs.python.org/3/library/importlib.html#module-importlib.resources -.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html -.. _`PEP 566`: https://www.python.org/dev/peps/pep-0566/ -.. _`ConfigParser instance`: https://docs.python.org/3/library/configparser.html#configparser.ConfigParser -.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders -.. _`meta path finders`: https://docs.python.org/3/glossary.html#term-meta-path-finder -.. _`sys.meta_path`: https://docs.python.org/3/library/sys.html#sys.meta_path diff --git a/libs/win/importlib_metadata/tests/test_api.py b/libs/win/importlib_metadata/tests/test_api.py deleted file mode 100644 index 82c61f51..00000000 --- a/libs/win/importlib_metadata/tests/test_api.py +++ /dev/null @@ -1,44 +0,0 @@ -import re -import unittest - -import importlib_metadata - - -class APITests(unittest.TestCase): - version_pattern = r'\d+\.\d+(\.\d)?' - - def test_retrieves_version_of_self(self): - version = importlib_metadata.version('importlib_metadata') - assert isinstance(version, str) - assert re.match(self.version_pattern, version) - - def test_retrieves_version_of_pip(self): - # Assume pip is installed and retrieve the version of pip. - version = importlib_metadata.version('pip') - assert isinstance(version, str) - assert re.match(self.version_pattern, version) - - def test_for_name_does_not_exist(self): - with self.assertRaises(importlib_metadata.PackageNotFoundError): - importlib_metadata.distribution('does-not-exist') - - def test_for_top_level(self): - distribution = importlib_metadata.distribution('importlib_metadata') - self.assertEqual( - distribution.read_text('top_level.txt').strip(), - 'importlib_metadata') - - def test_entry_points(self): - parser = importlib_metadata.entry_points('pip') - # We should probably not be dependent on a third party package's - # internal API staying stable. - entry_point = parser.get('console_scripts', 'pip') - self.assertEqual(entry_point, 'pip._internal:main') - - def test_metadata_for_this_package(self): - md = importlib_metadata.metadata('importlib_metadata') - assert md['author'] == 'Barry Warsaw' - assert md['LICENSE'] == 'Apache Software License' - assert md['Name'] == 'importlib-metadata' - classifiers = md.get_all('Classifier') - assert 'Topic :: Software Development :: Libraries' in classifiers diff --git a/libs/win/importlib_metadata/tests/test_main.py b/libs/win/importlib_metadata/tests/test_main.py deleted file mode 100644 index 381e4dae..00000000 --- a/libs/win/importlib_metadata/tests/test_main.py +++ /dev/null @@ -1,121 +0,0 @@ -from __future__ import unicode_literals - -import re -import sys -import shutil -import tempfile -import unittest -import importlib -import contextlib -import importlib_metadata - -try: - from contextlib import ExitStack -except ImportError: - from contextlib2 import ExitStack - -try: - import pathlib -except ImportError: - import pathlib2 as pathlib - -from importlib_metadata import _hooks - - -class BasicTests(unittest.TestCase): - version_pattern = r'\d+\.\d+(\.\d)?' - - def test_retrieves_version_of_pip(self): - # Assume pip is installed and retrieve the version of pip. - dist = importlib_metadata.Distribution.from_name('pip') - assert isinstance(dist.version, str) - assert re.match(self.version_pattern, dist.version) - - def test_for_name_does_not_exist(self): - with self.assertRaises(importlib_metadata.PackageNotFoundError): - importlib_metadata.Distribution.from_name('does-not-exist') - - def test_new_style_classes(self): - self.assertIsInstance(importlib_metadata.Distribution, type) - self.assertIsInstance(_hooks.MetadataPathFinder, type) - self.assertIsInstance(_hooks.WheelMetadataFinder, type) - self.assertIsInstance(_hooks.WheelDistribution, type) - - -class ImportTests(unittest.TestCase): - def test_import_nonexistent_module(self): - # Ensure that the MetadataPathFinder does not crash an import of a - # non-existant module. - with self.assertRaises(ImportError): - importlib.import_module('does_not_exist') - - def test_resolve(self): - entry_points = importlib_metadata.entry_points('pip') - main = importlib_metadata.resolve( - entry_points.get('console_scripts', 'pip')) - import pip._internal - self.assertEqual(main, pip._internal.main) - - def test_resolve_invalid(self): - self.assertRaises(ValueError, importlib_metadata.resolve, 'bogus.ep') - - -class NameNormalizationTests(unittest.TestCase): - @staticmethod - def pkg_with_dashes(site_dir): - """ - Create minimal metadata for a package with dashes - in the name (and thus underscores in the filename). - """ - metadata_dir = site_dir / 'my_pkg.dist-info' - metadata_dir.mkdir() - metadata = metadata_dir / 'METADATA' - with metadata.open('w') as strm: - strm.write('Version: 1.0\n') - return 'my-pkg' - - @staticmethod - @contextlib.contextmanager - def site_dir(): - tmpdir = tempfile.mkdtemp() - sys.path[:0] = [tmpdir] - try: - yield pathlib.Path(tmpdir) - finally: - sys.path.remove(tmpdir) - shutil.rmtree(tmpdir) - - def setUp(self): - self.fixtures = ExitStack() - self.addCleanup(self.fixtures.close) - self.site_dir = self.fixtures.enter_context(self.site_dir()) - - def test_dashes_in_dist_name_found_as_underscores(self): - """ - For a package with a dash in the name, the dist-info metadata - uses underscores in the name. Ensure the metadata loads. - """ - pkg_name = self.pkg_with_dashes(self.site_dir) - assert importlib_metadata.version(pkg_name) == '1.0' - - @staticmethod - def pkg_with_mixed_case(site_dir): - """ - Create minimal metadata for a package with mixed case - in the name. - """ - metadata_dir = site_dir / 'CherryPy.dist-info' - metadata_dir.mkdir() - metadata = metadata_dir / 'METADATA' - with metadata.open('w') as strm: - strm.write('Version: 1.0\n') - return 'CherryPy' - - def test_dist_name_found_as_any_case(self): - """ - Ensure the metadata loads when queried with any case. - """ - pkg_name = self.pkg_with_mixed_case(self.site_dir) - assert importlib_metadata.version(pkg_name) == '1.0' - assert importlib_metadata.version(pkg_name.lower()) == '1.0' - assert importlib_metadata.version(pkg_name.upper()) == '1.0' diff --git a/libs/win/importlib_metadata/tests/test_zip.py b/libs/win/importlib_metadata/tests/test_zip.py deleted file mode 100644 index 7bdf55a9..00000000 --- a/libs/win/importlib_metadata/tests/test_zip.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys -import unittest -import importlib_metadata - -try: - from contextlib import ExitStack -except ImportError: - from contextlib2 import ExitStack - -from importlib_resources import path - - -class BespokeLoader: - archive = 'bespoke' - - -class TestZip(unittest.TestCase): - def setUp(self): - # Find the path to the example.*.whl so we can add it to the front of - # sys.path, where we'll then try to find the metadata thereof. - self.resources = ExitStack() - self.addCleanup(self.resources.close) - wheel = self.resources.enter_context( - path('importlib_metadata.tests.data', - 'example-21.12-py3-none-any.whl')) - sys.path.insert(0, str(wheel)) - self.resources.callback(sys.path.pop, 0) - - def test_zip_version(self): - self.assertEqual(importlib_metadata.version('example'), '21.12') - - def test_zip_entry_points(self): - parser = importlib_metadata.entry_points('example') - entry_point = parser.get('console_scripts', 'example') - self.assertEqual(entry_point, 'example:main') - - def test_missing_metadata(self): - distribution = importlib_metadata.distribution('example') - self.assertIsNone(distribution.read_text('does not exist')) - - def test_case_insensitive(self): - self.assertEqual(importlib_metadata.version('Example'), '21.12') diff --git a/libs/win/importlib_metadata/version.txt b/libs/win/importlib_metadata/version.txt deleted file mode 100644 index eb49d7c7..00000000 --- a/libs/win/importlib_metadata/version.txt +++ /dev/null @@ -1 +0,0 @@ -0.7 diff --git a/libs/win/importlib_resources/__init__.py b/libs/win/importlib_resources/__init__.py new file mode 100644 index 00000000..34e3a995 --- /dev/null +++ b/libs/win/importlib_resources/__init__.py @@ -0,0 +1,36 @@ +"""Read resources contained within a package.""" + +from ._common import ( + as_file, + files, + Package, +) + +from ._legacy import ( + contents, + open_binary, + read_binary, + open_text, + read_text, + is_resource, + path, + Resource, +) + +from .abc import ResourceReader + + +__all__ = [ + 'Package', + 'Resource', + 'ResourceReader', + 'as_file', + 'contents', + 'files', + 'is_resource', + 'open_binary', + 'open_text', + 'path', + 'read_binary', + 'read_text', +] diff --git a/libs/win/importlib_resources/_adapters.py b/libs/win/importlib_resources/_adapters.py new file mode 100644 index 00000000..ea363d86 --- /dev/null +++ b/libs/win/importlib_resources/_adapters.py @@ -0,0 +1,170 @@ +from contextlib import suppress +from io import TextIOWrapper + +from . import abc + + +class SpecLoaderAdapter: + """ + Adapt a package spec to adapt the underlying loader. + """ + + def __init__(self, spec, adapter=lambda spec: spec.loader): + self.spec = spec + self.loader = adapter(spec) + + def __getattr__(self, name): + return getattr(self.spec, name) + + +class TraversableResourcesLoader: + """ + Adapt a loader to provide TraversableResources. + """ + + def __init__(self, spec): + self.spec = spec + + def get_resource_reader(self, name): + return CompatibilityFiles(self.spec)._native() + + +def _io_wrapper(file, mode='r', *args, **kwargs): + if mode == 'r': + return TextIOWrapper(file, *args, **kwargs) + elif mode == 'rb': + return file + raise ValueError( + "Invalid mode value '{}', only 'r' and 'rb' are supported".format(mode) + ) + + +class CompatibilityFiles: + """ + Adapter for an existing or non-existent resource reader + to provide a compatibility .files(). + """ + + class SpecPath(abc.Traversable): + """ + Path tied to a module spec. + Can be read and exposes the resource reader children. + """ + + def __init__(self, spec, reader): + self._spec = spec + self._reader = reader + + def iterdir(self): + if not self._reader: + return iter(()) + return iter( + CompatibilityFiles.ChildPath(self._reader, path) + for path in self._reader.contents() + ) + + def is_file(self): + return False + + is_dir = is_file + + def joinpath(self, other): + if not self._reader: + return CompatibilityFiles.OrphanPath(other) + return CompatibilityFiles.ChildPath(self._reader, other) + + @property + def name(self): + return self._spec.name + + def open(self, mode='r', *args, **kwargs): + return _io_wrapper(self._reader.open_resource(None), mode, *args, **kwargs) + + class ChildPath(abc.Traversable): + """ + Path tied to a resource reader child. + Can be read but doesn't expose any meaningful children. + """ + + def __init__(self, reader, name): + self._reader = reader + self._name = name + + def iterdir(self): + return iter(()) + + def is_file(self): + return self._reader.is_resource(self.name) + + def is_dir(self): + return not self.is_file() + + def joinpath(self, other): + return CompatibilityFiles.OrphanPath(self.name, other) + + @property + def name(self): + return self._name + + def open(self, mode='r', *args, **kwargs): + return _io_wrapper( + self._reader.open_resource(self.name), mode, *args, **kwargs + ) + + class OrphanPath(abc.Traversable): + """ + Orphan path, not tied to a module spec or resource reader. + Can't be read and doesn't expose any meaningful children. + """ + + def __init__(self, *path_parts): + if len(path_parts) < 1: + raise ValueError('Need at least one path part to construct a path') + self._path = path_parts + + def iterdir(self): + return iter(()) + + def is_file(self): + return False + + is_dir = is_file + + def joinpath(self, other): + return CompatibilityFiles.OrphanPath(*self._path, other) + + @property + def name(self): + return self._path[-1] + + def open(self, mode='r', *args, **kwargs): + raise FileNotFoundError("Can't open orphan path") + + def __init__(self, spec): + self.spec = spec + + @property + def _reader(self): + with suppress(AttributeError): + return self.spec.loader.get_resource_reader(self.spec.name) + + def _native(self): + """ + Return the native reader if it supports files(). + """ + reader = self._reader + return reader if hasattr(reader, 'files') else self + + def __getattr__(self, attr): + return getattr(self._reader, attr) + + def files(self): + return CompatibilityFiles.SpecPath(self.spec, self._reader) + + +def wrap_spec(package): + """ + Construct a package spec with traversable compatibility + on the spec/loader/reader. + """ + return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) diff --git a/libs/win/importlib_resources/_common.py b/libs/win/importlib_resources/_common.py new file mode 100644 index 00000000..9f19784d --- /dev/null +++ b/libs/win/importlib_resources/_common.py @@ -0,0 +1,207 @@ +import os +import pathlib +import tempfile +import functools +import contextlib +import types +import importlib +import inspect +import warnings +import itertools + +from typing import Union, Optional, cast +from .abc import ResourceReader, Traversable + +from ._compat import wrap_spec + +Package = Union[types.ModuleType, str] +Anchor = Package + + +def package_to_anchor(func): + """ + Replace 'package' parameter as 'anchor' and warn about the change. + + Other errors should fall through. + + >>> files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + +@package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. + """ + return from_package(resolve(anchor)) + + +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: + """ + Return the package's loader if it's a ResourceReader. + """ + # We can't use + # a issubclass() check here because apparently abc.'s __subclasscheck__() + # hook wants to create a weak reference to the object, but + # zipimport.zipimporter does not support weak references, resulting in a + # TypeError. That seems terrible. + spec = package.__spec__ + reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore + if reader is None: + return None + return reader(spec.name) # type: ignore + + +@functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + +@resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + + +@resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) + + +def _infer_caller(): + """ + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame + + +def from_package(package: types.ModuleType): + """ + Return a Traversable object for the given package. + + """ + spec = wrap_spec(package) + reader = spec.loader.get_resource_reader(spec.name) + return reader.files() + + +@contextlib.contextmanager +def _tempfile( + reader, + suffix='', + # gh-93353: Keep a reference to call os.remove() in late Python + # finalization. + *, + _os_remove=os.remove, +): + # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' + # blocks due to the need to close the temporary file to work on Windows + # properly. + fd, raw_path = tempfile.mkstemp(suffix=suffix) + try: + try: + os.write(fd, reader()) + finally: + os.close(fd) + del reader + yield pathlib.Path(raw_path) + finally: + try: + _os_remove(raw_path) + except FileNotFoundError: + pass + + +def _temp_file(path): + return _tempfile(path.read_bytes, suffix=path.name) + + +def _is_present_dir(path: Traversable) -> bool: + """ + Some Traversables implement ``is_dir()`` to raise an + exception (i.e. ``FileNotFoundError``) when the + directory doesn't exist. This function wraps that call + to always return a boolean and only return True + if there's a dir and it exists. + """ + with contextlib.suppress(FileNotFoundError): + return path.is_dir() + return False + + +@functools.singledispatch +def as_file(path): + """ + Given a Traversable object, return that object as a + path on the local file system in a context manager. + """ + return _temp_dir(path) if _is_present_dir(path) else _temp_file(path) + + +@as_file.register(pathlib.Path) +@contextlib.contextmanager +def _(path): + """ + Degenerate behavior for pathlib.Path objects. + """ + yield path + + +@contextlib.contextmanager +def _temp_path(dir: tempfile.TemporaryDirectory): + """ + Wrap tempfile.TemporyDirectory to return a pathlib object. + """ + with dir as result: + yield pathlib.Path(result) + + +@contextlib.contextmanager +def _temp_dir(path): + """ + Given a traversable dir, recursively replicate the whole tree + to the file system in a context manager. + """ + assert path.is_dir() + with _temp_path(tempfile.TemporaryDirectory()) as temp_dir: + yield _write_contents(temp_dir, path) + + +def _write_contents(target, source): + child = target.joinpath(source.name) + if source.is_dir(): + child.mkdir() + for item in source.iterdir(): + _write_contents(child, item) + else: + child.open('wb').write(source.read_bytes()) + return child diff --git a/libs/win/importlib_resources/_compat.py b/libs/win/importlib_resources/_compat.py new file mode 100644 index 00000000..8d7ade08 --- /dev/null +++ b/libs/win/importlib_resources/_compat.py @@ -0,0 +1,108 @@ +# flake8: noqa + +import abc +import os +import sys +import pathlib +from contextlib import suppress +from typing import Union + + +if sys.version_info >= (3, 10): + from zipfile import Path as ZipPath # type: ignore +else: + from zipp import Path as ZipPath # type: ignore + + +try: + from typing import runtime_checkable # type: ignore +except ImportError: + + def runtime_checkable(cls): # type: ignore + return cls + + +try: + from typing import Protocol # type: ignore +except ImportError: + Protocol = abc.ABC # type: ignore + + +class TraversableResourcesLoader: + """ + Adapt loaders to provide TraversableResources and other + compatibility. + + Used primarily for Python 3.9 and earlier where the native + loaders do not yet implement TraversableResources. + """ + + def __init__(self, spec): + self.spec = spec + + @property + def path(self): + return self.spec.origin + + def get_resource_reader(self, name): + from . import readers, _adapters + + def _zip_reader(spec): + with suppress(AttributeError): + return readers.ZipReader(spec.loader, spec.name) + + def _namespace_reader(spec): + with suppress(AttributeError, ValueError): + return readers.NamespaceReader(spec.submodule_search_locations) + + def _available_reader(spec): + with suppress(AttributeError): + return spec.loader.get_resource_reader(spec.name) + + def _native_reader(spec): + reader = _available_reader(spec) + return reader if hasattr(reader, 'files') else None + + def _file_reader(spec): + try: + path = pathlib.Path(self.path) + except TypeError: + return None + if path.exists(): + return readers.FileReader(self) + + return ( + # native reader if it supplies 'files' + _native_reader(self.spec) + or + # local ZipReader if a zip module + _zip_reader(self.spec) + or + # local NamespaceReader if a namespace module + _namespace_reader(self.spec) + or + # local FileReader + _file_reader(self.spec) + # fallback - adapt the spec ResourceReader to TraversableReader + or _adapters.CompatibilityFiles(self.spec) + ) + + +def wrap_spec(package): + """ + Construct a package spec with traversable compatibility + on the spec/loader/reader. + + Supersedes _adapters.wrap_spec to use TraversableResourcesLoader + from above for older Python compatibility (<3.10). + """ + from . import _adapters + + return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/libs/win/importlib_resources/_itertools.py b/libs/win/importlib_resources/_itertools.py new file mode 100644 index 00000000..cce05582 --- /dev/null +++ b/libs/win/importlib_resources/_itertools.py @@ -0,0 +1,35 @@ +from itertools import filterfalse + +from typing import ( + Callable, + Iterable, + Iterator, + Optional, + Set, + TypeVar, + Union, +) + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + + +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None +) -> Iterator[_T]: + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen: Set[Union[_T, _U]] = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element diff --git a/libs/win/importlib_resources/_legacy.py b/libs/win/importlib_resources/_legacy.py new file mode 100644 index 00000000..b1ea8105 --- /dev/null +++ b/libs/win/importlib_resources/_legacy.py @@ -0,0 +1,120 @@ +import functools +import os +import pathlib +import types +import warnings + +from typing import Union, Iterable, ContextManager, BinaryIO, TextIO, Any + +from . import _common + +Package = Union[types.ModuleType, str] +Resource = str + + +def deprecated(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + warnings.warn( + f"{func.__name__} is deprecated. Use files() instead. " + "Refer to https://importlib-resources.readthedocs.io" + "/en/latest/using.html#migrating-from-legacy for migration advice.", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + +def normalize_path(path: Any) -> str: + """Normalize a path by ensuring it is a string. + + If the resulting string contains path separators, an exception is raised. + """ + str_path = str(path) + parent, file_name = os.path.split(str_path) + if parent: + raise ValueError(f'{path!r} must be only a file name') + return file_name + + +@deprecated +def open_binary(package: Package, resource: Resource) -> BinaryIO: + """Return a file-like object opened for binary reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open('rb') + + +@deprecated +def read_binary(package: Package, resource: Resource) -> bytes: + """Return the binary contents of the resource.""" + return (_common.files(package) / normalize_path(resource)).read_bytes() + + +@deprecated +def open_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> TextIO: + """Return a file-like object opened for text reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open( + 'r', encoding=encoding, errors=errors + ) + + +@deprecated +def read_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> str: + """Return the decoded string of the resource. + + The decoding-related arguments have the same semantics as those of + bytes.decode(). + """ + with open_text(package, resource, encoding, errors) as fp: + return fp.read() + + +@deprecated +def contents(package: Package) -> Iterable[str]: + """Return an iterable of entries in `package`. + + Note that not all entries are resources. Specifically, directories are + not considered resources. Use `is_resource()` on each entry returned here + to check if it is a resource or not. + """ + return [path.name for path in _common.files(package).iterdir()] + + +@deprecated +def is_resource(package: Package, name: str) -> bool: + """True if `name` is a resource inside `package`. + + Directories are *not* resources. + """ + resource = normalize_path(name) + return any( + traversable.name == resource and traversable.is_file() + for traversable in _common.files(package).iterdir() + ) + + +@deprecated +def path( + package: Package, + resource: Resource, +) -> ContextManager[pathlib.Path]: + """A context manager providing a file path object to the resource. + + If the resource does not already exist on its own on the file system, + a temporary file will be created. If the file was created, the file + will be deleted upon exiting the context manager (no exception is + raised if the file was deleted prior to the context manager + exiting). + """ + return _common.as_file(_common.files(package) / normalize_path(resource)) diff --git a/libs/win/importlib_resources/abc.py b/libs/win/importlib_resources/abc.py new file mode 100644 index 00000000..23b6aeaf --- /dev/null +++ b/libs/win/importlib_resources/abc.py @@ -0,0 +1,170 @@ +import abc +import io +import itertools +import pathlib +from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional + +from ._compat import runtime_checkable, Protocol, StrPath + + +__all__ = ["ResourceReader", "Traversable", "TraversableResources"] + + +class ResourceReader(metaclass=abc.ABCMeta): + """Abstract base class for loaders to provide resource reading support.""" + + @abc.abstractmethod + def open_resource(self, resource: Text) -> BinaryIO: + """Return an opened, file-like object for binary reading. + + The 'resource' argument is expected to represent only a file name. + If the resource cannot be found, FileNotFoundError is raised. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def resource_path(self, resource: Text) -> Text: + """Return the file system path to the specified resource. + + The 'resource' argument is expected to represent only a file name. + If the resource does not exist on the file system, raise + FileNotFoundError. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def is_resource(self, path: Text) -> bool: + """Return True if the named 'path' is a resource. + + Files are resources, directories are not. + """ + raise FileNotFoundError + + @abc.abstractmethod + def contents(self) -> Iterable[str]: + """Return an iterable of entries in `package`.""" + raise FileNotFoundError + + +class TraversalError(Exception): + pass + + +@runtime_checkable +class Traversable(Protocol): + """ + An object with a subset of pathlib.Path methods suitable for + traversing directories and opening files. + + Any exceptions that occur when accessing the backing resource + may propagate unaltered. + """ + + @abc.abstractmethod + def iterdir(self) -> Iterator["Traversable"]: + """ + Yield Traversable objects in self + """ + + def read_bytes(self) -> bytes: + """ + Read contents of self as bytes + """ + with self.open('rb') as strm: + return strm.read() + + def read_text(self, encoding: Optional[str] = None) -> str: + """ + Read contents of self as text + """ + with self.open(encoding=encoding) as strm: + return strm.read() + + @abc.abstractmethod + def is_dir(self) -> bool: + """ + Return True if self is a directory + """ + + @abc.abstractmethod + def is_file(self) -> bool: + """ + Return True if self is a file + """ + + def joinpath(self, *descendants: StrPath) -> "Traversable": + """ + Return Traversable resolved with any descendants applied. + + Each descendant should be a path segment relative to self + and each may contain multiple levels separated by + ``posixpath.sep`` (``/``). + """ + if not descendants: + return self + names = itertools.chain.from_iterable( + path.parts for path in map(pathlib.PurePosixPath, descendants) + ) + target = next(names) + matches = ( + traversable for traversable in self.iterdir() if traversable.name == target + ) + try: + match = next(matches) + except StopIteration: + raise TraversalError( + "Target not found during traversal.", target, list(names) + ) + return match.joinpath(*names) + + def __truediv__(self, child: StrPath) -> "Traversable": + """ + Return Traversable child in self + """ + return self.joinpath(child) + + @abc.abstractmethod + def open(self, mode='r', *args, **kwargs): + """ + mode may be 'r' or 'rb' to open as text or binary. Return a handle + suitable for reading (same as pathlib.Path.open). + + When opening as text, accepts encoding parameters such as those + accepted by io.TextIOWrapper. + """ + + @property + @abc.abstractmethod + def name(self) -> str: + """ + The base name of this object without any parent references. + """ + + +class TraversableResources(ResourceReader): + """ + The required interface for providing traversable + resources. + """ + + @abc.abstractmethod + def files(self) -> "Traversable": + """Return a Traversable object for the loaded package.""" + + def open_resource(self, resource: StrPath) -> io.BufferedReader: + return self.files().joinpath(resource).open('rb') + + def resource_path(self, resource: Any) -> NoReturn: + raise FileNotFoundError(resource) + + def is_resource(self, path: StrPath) -> bool: + return self.files().joinpath(path).is_file() + + def contents(self) -> Iterator[str]: + return (item.name for item in self.files().iterdir()) diff --git a/libs/win/importlib_metadata/docs/__init__.py b/libs/win/importlib_resources/py.typed similarity index 100% rename from libs/win/importlib_metadata/docs/__init__.py rename to libs/win/importlib_resources/py.typed diff --git a/libs/win/importlib_resources/readers.py b/libs/win/importlib_resources/readers.py new file mode 100644 index 00000000..ab34db74 --- /dev/null +++ b/libs/win/importlib_resources/readers.py @@ -0,0 +1,120 @@ +import collections +import pathlib +import operator + +from . import abc + +from ._itertools import unique_everseen +from ._compat import ZipPath + + +def remove_duplicates(items): + return iter(collections.OrderedDict.fromkeys(items)) + + +class FileReader(abc.TraversableResources): + def __init__(self, loader): + self.path = pathlib.Path(loader.path).parent + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path + + +class ZipReader(abc.TraversableResources): + def __init__(self, loader, module): + _, _, name = module.rpartition('.') + self.prefix = loader.prefix.replace('\\', '/') + name + '/' + self.archive = loader.archive + + def open_resource(self, resource): + try: + return super().open_resource(resource) + except KeyError as exc: + raise FileNotFoundError(exc.args[0]) + + def is_resource(self, path): + # workaround for `zipfile.Path.is_file` returning true + # for non-existent paths. + target = self.files().joinpath(path) + return target.is_file() and target.exists() + + def files(self): + return ZipPath(self.archive, self.prefix) + + +class MultiplexedPath(abc.Traversable): + """ + Given a series of Traversable objects, implement a merged + version of the interface across all objects. Useful for + namespace packages which may be multihomed at a single + name. + """ + + def __init__(self, *paths): + self._paths = list(map(pathlib.Path, remove_duplicates(paths))) + if not self._paths: + message = 'MultiplexedPath must contain at least one path' + raise FileNotFoundError(message) + if not all(path.is_dir() for path in self._paths): + raise NotADirectoryError('MultiplexedPath only supports directories') + + def iterdir(self): + files = (file for path in self._paths for file in path.iterdir()) + return unique_everseen(files, key=operator.attrgetter('name')) + + def read_bytes(self): + raise FileNotFoundError(f'{self} is not a file') + + def read_text(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + def is_dir(self): + return True + + def is_file(self): + return False + + def joinpath(self, *descendants): + try: + return super().joinpath(*descendants) + except abc.TraversalError: + # One of the paths did not resolve (a directory does not exist). + # Just return something that will not exist. + return self._paths[0].joinpath(*descendants) + + def open(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + @property + def name(self): + return self._paths[0].name + + def __repr__(self): + paths = ', '.join(f"'{path}'" for path in self._paths) + return f'MultiplexedPath({paths})' + + +class NamespaceReader(abc.TraversableResources): + def __init__(self, namespace_path): + if 'NamespacePath' not in str(namespace_path): + raise ValueError('Invalid path') + self.path = MultiplexedPath(*list(namespace_path)) + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path diff --git a/libs/win/importlib_resources/simple.py b/libs/win/importlib_resources/simple.py new file mode 100644 index 00000000..7770c922 --- /dev/null +++ b/libs/win/importlib_resources/simple.py @@ -0,0 +1,106 @@ +""" +Interface adapters for low-level readers. +""" + +import abc +import io +import itertools +from typing import BinaryIO, List + +from .abc import Traversable, TraversableResources + + +class SimpleReader(abc.ABC): + """ + The minimum, low-level interface required from a resource + provider. + """ + + @property + @abc.abstractmethod + def package(self) -> str: + """ + The name of the package for which this reader loads resources. + """ + + @abc.abstractmethod + def children(self) -> List['SimpleReader']: + """ + Obtain an iterable of SimpleReader for available + child containers (e.g. directories). + """ + + @abc.abstractmethod + def resources(self) -> List[str]: + """ + Obtain available named resources for this virtual package. + """ + + @abc.abstractmethod + def open_binary(self, resource: str) -> BinaryIO: + """ + Obtain a File-like for a named resource. + """ + + @property + def name(self): + return self.package.split('.')[-1] + + +class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + + def __init__(self, reader: SimpleReader): + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = (ResourceHandle(self, name) for name in self.reader.resources) + dirs = map(ResourceContainer, self.reader.children()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent: ResourceContainer, name: str): + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + + def joinpath(self, name): + raise RuntimeError("Cannot traverse into a resource") + + +class TraversableReader(TraversableResources, SimpleReader): + """ + A TraversableResources based on SimpleReader. Resource providers + may derive from this class to provide the TraversableResources + interface by supplying the SimpleReader interface. + """ + + def files(self): + return ResourceContainer(self) diff --git a/libs/win/importlib_metadata/tests/__init__.py b/libs/win/importlib_resources/tests/__init__.py similarity index 100% rename from libs/win/importlib_metadata/tests/__init__.py rename to libs/win/importlib_resources/tests/__init__.py diff --git a/libs/win/importlib_resources/tests/_compat.py b/libs/win/importlib_resources/tests/_compat.py new file mode 100644 index 00000000..e7bf06dd --- /dev/null +++ b/libs/win/importlib_resources/tests/_compat.py @@ -0,0 +1,32 @@ +import os + + +try: + from test.support import import_helper # type: ignore +except ImportError: + # Python 3.9 and earlier + class import_helper: # type: ignore + from test.support import ( + modules_setup, + modules_cleanup, + DirsOnSysPath, + CleanImport, + ) + + +try: + from test.support import os_helper # type: ignore +except ImportError: + # Python 3.9 compat + class os_helper: # type:ignore + from test.support import temp_dir + + +try: + # Python 3.10 + from test.support.os_helper import unlink +except ImportError: + from test.support import unlink as _unlink + + def unlink(target): + return _unlink(os.fspath(target)) diff --git a/libs/win/importlib_resources/tests/_path.py b/libs/win/importlib_resources/tests/_path.py new file mode 100644 index 00000000..c630e4d3 --- /dev/null +++ b/libs/win/importlib_resources/tests/_path.py @@ -0,0 +1,50 @@ +import pathlib +import functools + + +#### +# from jaraco.path 3.4 + + +def build(spec, prefix=pathlib.Path()): + """ + Build a set of files/directories, as described by the spec. + + Each key represents a pathname, and the value represents + the content. Content may be a nested directory. + + >>> spec = { + ... 'README.txt': "A README file", + ... "foo": { + ... "__init__.py": "", + ... "bar": { + ... "__init__.py": "", + ... }, + ... "baz.py": "# Some code", + ... } + ... } + >>> tmpdir = getfixture('tmpdir') + >>> build(spec, tmpdir) + """ + for name, contents in spec.items(): + create(contents, pathlib.Path(prefix) / name) + + +@functools.singledispatch +def create(content, path): + path.mkdir(exist_ok=True) + build(content, prefix=path) # type: ignore + + +@create.register +def _(content: bytes, path): + path.write_bytes(content) + + +@create.register +def _(content: str, path): + path.write_text(content) + + +# end from jaraco.path +#### diff --git a/libs/win/importlib_metadata/tests/data/__init__.py b/libs/win/importlib_resources/tests/data01/__init__.py similarity index 100% rename from libs/win/importlib_metadata/tests/data/__init__.py rename to libs/win/importlib_resources/tests/data01/__init__.py diff --git a/libs/win/importlib_resources/tests/data01/binary.file b/libs/win/importlib_resources/tests/data01/binary.file new file mode 100644 index 0000000000000000000000000000000000000000..eaf36c1daccfdf325514461cd1a2ffbc139b5464 GIT binary patch literal 4 LcmZQzWMT#Y01f~L literal 0 HcmV?d00001 diff --git a/libs/win/more_itertools/tests/__init__.py b/libs/win/importlib_resources/tests/data01/subdirectory/__init__.py similarity index 100% rename from libs/win/more_itertools/tests/__init__.py rename to libs/win/importlib_resources/tests/data01/subdirectory/__init__.py diff --git a/libs/win/importlib_resources/tests/data01/subdirectory/binary.file b/libs/win/importlib_resources/tests/data01/subdirectory/binary.file new file mode 100644 index 0000000000000000000000000000000000000000..eaf36c1daccfdf325514461cd1a2ffbc139b5464 GIT binary patch literal 4 LcmZQzWMT#Y01f~L literal 0 HcmV?d00001 diff --git a/libs/win/importlib_resources/tests/data01/utf-16.file b/libs/win/importlib_resources/tests/data01/utf-16.file new file mode 100644 index 0000000000000000000000000000000000000000..2cb772295ef4b480a8d83725bd5006a0236d8f68 GIT binary patch literal 44 ucmezW&x0YAAqNQa8FUyF7(y9B7~B|i84MZBfV^^`Xc15@g+Y;liva-T)Ce>H literal 0 HcmV?d00001 diff --git a/libs/win/importlib_resources/tests/data01/utf-8.file b/libs/win/importlib_resources/tests/data01/utf-8.file new file mode 100644 index 00000000..1c0132ad --- /dev/null +++ b/libs/win/importlib_resources/tests/data01/utf-8.file @@ -0,0 +1 @@ +Hello, UTF-8 world! diff --git a/libs/win/importlib_resources/tests/data02/__init__.py b/libs/win/importlib_resources/tests/data02/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/importlib_resources/tests/data02/one/__init__.py b/libs/win/importlib_resources/tests/data02/one/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/importlib_resources/tests/data02/one/resource1.txt b/libs/win/importlib_resources/tests/data02/one/resource1.txt new file mode 100644 index 00000000..61a813e4 --- /dev/null +++ b/libs/win/importlib_resources/tests/data02/one/resource1.txt @@ -0,0 +1 @@ +one resource diff --git a/libs/win/importlib_resources/tests/data02/two/__init__.py b/libs/win/importlib_resources/tests/data02/two/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/importlib_resources/tests/data02/two/resource2.txt b/libs/win/importlib_resources/tests/data02/two/resource2.txt new file mode 100644 index 00000000..a80ce46e --- /dev/null +++ b/libs/win/importlib_resources/tests/data02/two/resource2.txt @@ -0,0 +1 @@ +two resource diff --git a/libs/win/importlib_resources/tests/namespacedata01/binary.file b/libs/win/importlib_resources/tests/namespacedata01/binary.file new file mode 100644 index 0000000000000000000000000000000000000000..eaf36c1daccfdf325514461cd1a2ffbc139b5464 GIT binary patch literal 4 LcmZQzWMT#Y01f~L literal 0 HcmV?d00001 diff --git a/libs/win/importlib_resources/tests/namespacedata01/utf-16.file b/libs/win/importlib_resources/tests/namespacedata01/utf-16.file new file mode 100644 index 0000000000000000000000000000000000000000..2cb772295ef4b480a8d83725bd5006a0236d8f68 GIT binary patch literal 44 ucmezW&x0YAAqNQa8FUyF7(y9B7~B|i84MZBfV^^`Xc15@g+Y;liva-T)Ce>H literal 0 HcmV?d00001 diff --git a/libs/win/importlib_resources/tests/namespacedata01/utf-8.file b/libs/win/importlib_resources/tests/namespacedata01/utf-8.file new file mode 100644 index 00000000..1c0132ad --- /dev/null +++ b/libs/win/importlib_resources/tests/namespacedata01/utf-8.file @@ -0,0 +1 @@ +Hello, UTF-8 world! diff --git a/libs/win/importlib_resources/tests/test_compatibilty_files.py b/libs/win/importlib_resources/tests/test_compatibilty_files.py new file mode 100644 index 00000000..d92c7c56 --- /dev/null +++ b/libs/win/importlib_resources/tests/test_compatibilty_files.py @@ -0,0 +1,102 @@ +import io +import unittest + +import importlib_resources as resources + +from importlib_resources._adapters import ( + CompatibilityFiles, + wrap_spec, +) + +from . import util + + +class CompatibilityFilesTests(unittest.TestCase): + @property + def package(self): + bytes_data = io.BytesIO(b'Hello, world!') + return util.create_package( + file=bytes_data, + path='some_path', + contents=('a', 'b', 'c'), + ) + + @property + def files(self): + return resources.files(self.package) + + def test_spec_path_iter(self): + self.assertEqual( + sorted(path.name for path in self.files.iterdir()), + ['a', 'b', 'c'], + ) + + def test_child_path_iter(self): + self.assertEqual(list((self.files / 'a').iterdir()), []) + + def test_orphan_path_iter(self): + self.assertEqual(list((self.files / 'a' / 'a').iterdir()), []) + self.assertEqual(list((self.files / 'a' / 'a' / 'a').iterdir()), []) + + def test_spec_path_is(self): + self.assertFalse(self.files.is_file()) + self.assertFalse(self.files.is_dir()) + + def test_child_path_is(self): + self.assertTrue((self.files / 'a').is_file()) + self.assertFalse((self.files / 'a').is_dir()) + + def test_orphan_path_is(self): + self.assertFalse((self.files / 'a' / 'a').is_file()) + self.assertFalse((self.files / 'a' / 'a').is_dir()) + self.assertFalse((self.files / 'a' / 'a' / 'a').is_file()) + self.assertFalse((self.files / 'a' / 'a' / 'a').is_dir()) + + def test_spec_path_name(self): + self.assertEqual(self.files.name, 'testingpackage') + + def test_child_path_name(self): + self.assertEqual((self.files / 'a').name, 'a') + + def test_orphan_path_name(self): + self.assertEqual((self.files / 'a' / 'b').name, 'b') + self.assertEqual((self.files / 'a' / 'b' / 'c').name, 'c') + + def test_spec_path_open(self): + self.assertEqual(self.files.read_bytes(), b'Hello, world!') + self.assertEqual(self.files.read_text(), 'Hello, world!') + + def test_child_path_open(self): + self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!') + self.assertEqual((self.files / 'a').read_text(), 'Hello, world!') + + def test_orphan_path_open(self): + with self.assertRaises(FileNotFoundError): + (self.files / 'a' / 'b').read_bytes() + with self.assertRaises(FileNotFoundError): + (self.files / 'a' / 'b' / 'c').read_bytes() + + def test_open_invalid_mode(self): + with self.assertRaises(ValueError): + self.files.open('0') + + def test_orphan_path_invalid(self): + with self.assertRaises(ValueError): + CompatibilityFiles.OrphanPath() + + def test_wrap_spec(self): + spec = wrap_spec(self.package) + self.assertIsInstance(spec.loader.get_resource_reader(None), CompatibilityFiles) + + +class CompatibilityFilesNoReaderTests(unittest.TestCase): + @property + def package(self): + return util.create_package_from_loader(None) + + @property + def files(self): + return resources.files(self.package) + + def test_spec_path_joinpath(self): + self.assertIsInstance(self.files / 'a', CompatibilityFiles.OrphanPath) diff --git a/libs/win/importlib_resources/tests/test_contents.py b/libs/win/importlib_resources/tests/test_contents.py new file mode 100644 index 00000000..525568e8 --- /dev/null +++ b/libs/win/importlib_resources/tests/test_contents.py @@ -0,0 +1,43 @@ +import unittest +import importlib_resources as resources + +from . import data01 +from . import util + + +class ContentsTests: + expected = { + '__init__.py', + 'binary.file', + 'subdirectory', + 'utf-16.file', + 'utf-8.file', + } + + def test_contents(self): + contents = {path.name for path in resources.files(self.data).iterdir()} + assert self.expected <= contents + + +class ContentsDiskTests(ContentsTests, unittest.TestCase): + def setUp(self): + self.data = data01 + + +class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase): + pass + + +class ContentsNamespaceTests(ContentsTests, unittest.TestCase): + expected = { + # no __init__ because of namespace design + # no subdirectory as incidental difference in fixture + 'binary.file', + 'utf-16.file', + 'utf-8.file', + } + + def setUp(self): + from . import namespacedata01 + + self.data = namespacedata01 diff --git a/libs/win/importlib_resources/tests/test_files.py b/libs/win/importlib_resources/tests/test_files.py new file mode 100644 index 00000000..d258fb5f --- /dev/null +++ b/libs/win/importlib_resources/tests/test_files.py @@ -0,0 +1,112 @@ +import typing +import textwrap +import unittest +import warnings +import importlib +import contextlib + +import importlib_resources as resources +from ..abc import Traversable +from . import data01 +from . import util +from . import _path +from ._compat import os_helper, import_helper + + +@contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(record=True) as ctx: + warnings.simplefilter('default', category=DeprecationWarning) + yield ctx + + +class FilesTests: + def test_read_bytes(self): + files = resources.files(self.data) + actual = files.joinpath('utf-8.file').read_bytes() + assert actual == b'Hello, UTF-8 world!\n' + + def test_read_text(self): + files = resources.files(self.data) + actual = files.joinpath('utf-8.file').read_text(encoding='utf-8') + assert actual == 'Hello, UTF-8 world!\n' + + @unittest.skipUnless( + hasattr(typing, 'runtime_checkable'), + "Only suitable when typing supports runtime_checkable", + ) + def test_traversable(self): + assert isinstance(resources.files(self.data), Traversable) + + def test_old_parameter(self): + """ + Files used to take a 'package' parameter. Make sure anyone + passing by name is still supported. + """ + with suppress_known_deprecation(): + resources.files(package=self.data) + + +class OpenDiskTests(FilesTests, unittest.TestCase): + def setUp(self): + self.data = data01 + + +class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase): + pass + + +class OpenNamespaceTests(FilesTests, unittest.TestCase): + def setUp(self): + from . import namespacedata01 + + self.data = namespacedata01 + + +class SiteDir: + def setUp(self): + self.fixtures = contextlib.ExitStack() + self.addCleanup(self.fixtures.close) + self.site_dir = self.fixtures.enter_context(os_helper.temp_dir()) + self.fixtures.enter_context(import_helper.DirsOnSysPath(self.site_dir)) + self.fixtures.enter_context(import_helper.CleanImport()) + + +class ModulesFilesTests(SiteDir, unittest.TestCase): + def test_module_resources(self): + """ + A module can have resources found adjacent to the module. + """ + spec = { + 'mod.py': '', + 'res.txt': 'resources are the best', + } + _path.build(spec, self.site_dir) + import mod + + actual = resources.files(mod).joinpath('res.txt').read_text() + assert actual == spec['res.txt'] + + +class ImplicitContextFilesTests(SiteDir, unittest.TestCase): + def test_implicit_files(self): + """ + Without any parameter, files() will infer the location as the caller. + """ + spec = { + 'somepkg': { + '__init__.py': textwrap.dedent( + """ + import importlib_resources as res + val = res.files().joinpath('res.txt').read_text() + """ + ), + 'res.txt': 'resources are the best', + }, + } + _path.build(spec, self.site_dir) + assert importlib.import_module('somepkg').val == 'resources are the best' + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/test_open.py b/libs/win/importlib_resources/tests/test_open.py new file mode 100644 index 00000000..87b42c3d --- /dev/null +++ b/libs/win/importlib_resources/tests/test_open.py @@ -0,0 +1,81 @@ +import unittest + +import importlib_resources as resources +from . import data01 +from . import util + + +class CommonBinaryTests(util.CommonTests, unittest.TestCase): + def execute(self, package, path): + target = resources.files(package).joinpath(path) + with target.open('rb'): + pass + + +class CommonTextTests(util.CommonTests, unittest.TestCase): + def execute(self, package, path): + target = resources.files(package).joinpath(path) + with target.open(): + pass + + +class OpenTests: + def test_open_binary(self): + target = resources.files(self.data) / 'binary.file' + with target.open('rb') as fp: + result = fp.read() + self.assertEqual(result, b'\x00\x01\x02\x03') + + def test_open_text_default_encoding(self): + target = resources.files(self.data) / 'utf-8.file' + with target.open() as fp: + result = fp.read() + self.assertEqual(result, 'Hello, UTF-8 world!\n') + + def test_open_text_given_encoding(self): + target = resources.files(self.data) / 'utf-16.file' + with target.open(encoding='utf-16', errors='strict') as fp: + result = fp.read() + self.assertEqual(result, 'Hello, UTF-16 world!\n') + + def test_open_text_with_errors(self): + # Raises UnicodeError without the 'errors' argument. + target = resources.files(self.data) / 'utf-16.file' + with target.open(encoding='utf-8', errors='strict') as fp: + self.assertRaises(UnicodeError, fp.read) + with target.open(encoding='utf-8', errors='ignore') as fp: + result = fp.read() + self.assertEqual( + result, + 'H\x00e\x00l\x00l\x00o\x00,\x00 ' + '\x00U\x00T\x00F\x00-\x001\x006\x00 ' + '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00', + ) + + def test_open_binary_FileNotFoundError(self): + target = resources.files(self.data) / 'does-not-exist' + self.assertRaises(FileNotFoundError, target.open, 'rb') + + def test_open_text_FileNotFoundError(self): + target = resources.files(self.data) / 'does-not-exist' + self.assertRaises(FileNotFoundError, target.open) + + +class OpenDiskTests(OpenTests, unittest.TestCase): + def setUp(self): + self.data = data01 + + +class OpenDiskNamespaceTests(OpenTests, unittest.TestCase): + def setUp(self): + from . import namespacedata01 + + self.data = namespacedata01 + + +class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase): + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/test_path.py b/libs/win/importlib_resources/tests/test_path.py new file mode 100644 index 00000000..4f4d3943 --- /dev/null +++ b/libs/win/importlib_resources/tests/test_path.py @@ -0,0 +1,64 @@ +import io +import unittest + +import importlib_resources as resources +from . import data01 +from . import util + + +class CommonTests(util.CommonTests, unittest.TestCase): + def execute(self, package, path): + with resources.as_file(resources.files(package).joinpath(path)): + pass + + +class PathTests: + def test_reading(self): + # Path should be readable. + # Test also implicitly verifies the returned object is a pathlib.Path + # instance. + target = resources.files(self.data) / 'utf-8.file' + with resources.as_file(target) as path: + self.assertTrue(path.name.endswith("utf-8.file"), repr(path)) + # pathlib.Path.read_text() was introduced in Python 3.5. + with path.open('r', encoding='utf-8') as file: + text = file.read() + self.assertEqual('Hello, UTF-8 world!\n', text) + + +class PathDiskTests(PathTests, unittest.TestCase): + data = data01 + + def test_natural_path(self): + """ + Guarantee the internal implementation detail that + file-system-backed resources do not get the tempdir + treatment. + """ + target = resources.files(self.data) / 'utf-8.file' + with resources.as_file(target) as path: + assert 'data' in str(path) + + +class PathMemoryTests(PathTests, unittest.TestCase): + def setUp(self): + file = io.BytesIO(b'Hello, UTF-8 world!\n') + self.addCleanup(file.close) + self.data = util.create_package( + file=file, path=FileNotFoundError("package exists only in memory") + ) + self.data.__spec__.origin = None + self.data.__spec__.has_location = False + + +class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase): + def test_remove_in_context_manager(self): + # It is not an error if the file that was temporarily stashed on the + # file system is removed inside the `with` stanza. + target = resources.files(self.data) / 'utf-8.file' + with resources.as_file(target) as path: + path.unlink() + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/test_read.py b/libs/win/importlib_resources/tests/test_read.py new file mode 100644 index 00000000..41dd6db5 --- /dev/null +++ b/libs/win/importlib_resources/tests/test_read.py @@ -0,0 +1,76 @@ +import unittest +import importlib_resources as resources + +from . import data01 +from . import util +from importlib import import_module + + +class CommonBinaryTests(util.CommonTests, unittest.TestCase): + def execute(self, package, path): + resources.files(package).joinpath(path).read_bytes() + + +class CommonTextTests(util.CommonTests, unittest.TestCase): + def execute(self, package, path): + resources.files(package).joinpath(path).read_text() + + +class ReadTests: + def test_read_bytes(self): + result = resources.files(self.data).joinpath('binary.file').read_bytes() + self.assertEqual(result, b'\0\1\2\3') + + def test_read_text_default_encoding(self): + result = resources.files(self.data).joinpath('utf-8.file').read_text() + self.assertEqual(result, 'Hello, UTF-8 world!\n') + + def test_read_text_given_encoding(self): + result = ( + resources.files(self.data) + .joinpath('utf-16.file') + .read_text(encoding='utf-16') + ) + self.assertEqual(result, 'Hello, UTF-16 world!\n') + + def test_read_text_with_errors(self): + # Raises UnicodeError without the 'errors' argument. + target = resources.files(self.data) / 'utf-16.file' + self.assertRaises(UnicodeError, target.read_text, encoding='utf-8') + result = target.read_text(encoding='utf-8', errors='ignore') + self.assertEqual( + result, + 'H\x00e\x00l\x00l\x00o\x00,\x00 ' + '\x00U\x00T\x00F\x00-\x001\x006\x00 ' + '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00', + ) + + +class ReadDiskTests(ReadTests, unittest.TestCase): + data = data01 + + +class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase): + def test_read_submodule_resource(self): + submodule = import_module('ziptestdata.subdirectory') + result = resources.files(submodule).joinpath('binary.file').read_bytes() + self.assertEqual(result, b'\0\1\2\3') + + def test_read_submodule_resource_by_name(self): + result = ( + resources.files('ziptestdata.subdirectory') + .joinpath('binary.file') + .read_bytes() + ) + self.assertEqual(result, b'\0\1\2\3') + + +class ReadNamespaceTests(ReadTests, unittest.TestCase): + def setUp(self): + from . import namespacedata01 + + self.data = namespacedata01 + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/test_reader.py b/libs/win/importlib_resources/tests/test_reader.py new file mode 100644 index 00000000..1c8ebeeb --- /dev/null +++ b/libs/win/importlib_resources/tests/test_reader.py @@ -0,0 +1,133 @@ +import os.path +import sys +import pathlib +import unittest + +from importlib import import_module +from importlib_resources.readers import MultiplexedPath, NamespaceReader + + +class MultiplexedPathTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + path = pathlib.Path(__file__).parent / 'namespacedata01' + cls.folder = str(path) + + def test_init_no_paths(self): + with self.assertRaises(FileNotFoundError): + MultiplexedPath() + + def test_init_file(self): + with self.assertRaises(NotADirectoryError): + MultiplexedPath(os.path.join(self.folder, 'binary.file')) + + def test_iterdir(self): + contents = {path.name for path in MultiplexedPath(self.folder).iterdir()} + try: + contents.remove('__pycache__') + except (KeyError, ValueError): + pass + self.assertEqual(contents, {'binary.file', 'utf-16.file', 'utf-8.file'}) + + def test_iterdir_duplicate(self): + data01 = os.path.abspath(os.path.join(__file__, '..', 'data01')) + contents = { + path.name for path in MultiplexedPath(self.folder, data01).iterdir() + } + for remove in ('__pycache__', '__init__.pyc'): + try: + contents.remove(remove) + except (KeyError, ValueError): + pass + self.assertEqual( + contents, + {'__init__.py', 'binary.file', 'subdirectory', 'utf-16.file', 'utf-8.file'}, + ) + + def test_is_dir(self): + self.assertEqual(MultiplexedPath(self.folder).is_dir(), True) + + def test_is_file(self): + self.assertEqual(MultiplexedPath(self.folder).is_file(), False) + + def test_open_file(self): + path = MultiplexedPath(self.folder) + with self.assertRaises(FileNotFoundError): + path.read_bytes() + with self.assertRaises(FileNotFoundError): + path.read_text() + with self.assertRaises(FileNotFoundError): + path.open() + + def test_join_path(self): + prefix = os.path.abspath(os.path.join(__file__, '..')) + data01 = os.path.join(prefix, 'data01') + path = MultiplexedPath(self.folder, data01) + self.assertEqual( + str(path.joinpath('binary.file'))[len(prefix) + 1 :], + os.path.join('namespacedata01', 'binary.file'), + ) + self.assertEqual( + str(path.joinpath('subdirectory'))[len(prefix) + 1 :], + os.path.join('data01', 'subdirectory'), + ) + self.assertEqual( + str(path.joinpath('imaginary'))[len(prefix) + 1 :], + os.path.join('namespacedata01', 'imaginary'), + ) + self.assertEqual(path.joinpath(), path) + + def test_join_path_compound(self): + path = MultiplexedPath(self.folder) + assert not path.joinpath('imaginary/foo.py').exists() + + def test_repr(self): + self.assertEqual( + repr(MultiplexedPath(self.folder)), + f"MultiplexedPath('{self.folder}')", + ) + + def test_name(self): + self.assertEqual( + MultiplexedPath(self.folder).name, + os.path.basename(self.folder), + ) + + +class NamespaceReaderTest(unittest.TestCase): + site_dir = str(pathlib.Path(__file__).parent) + + @classmethod + def setUpClass(cls): + sys.path.append(cls.site_dir) + + @classmethod + def tearDownClass(cls): + sys.path.remove(cls.site_dir) + + def test_init_error(self): + with self.assertRaises(ValueError): + NamespaceReader(['path1', 'path2']) + + def test_resource_path(self): + namespacedata01 = import_module('namespacedata01') + reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations) + + root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01')) + self.assertEqual( + reader.resource_path('binary.file'), os.path.join(root, 'binary.file') + ) + self.assertEqual( + reader.resource_path('imaginary'), os.path.join(root, 'imaginary') + ) + + def test_files(self): + namespacedata01 = import_module('namespacedata01') + reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations) + root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01')) + self.assertIsInstance(reader.files(), MultiplexedPath) + self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')") + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/test_resource.py b/libs/win/importlib_resources/tests/test_resource.py new file mode 100644 index 00000000..82390271 --- /dev/null +++ b/libs/win/importlib_resources/tests/test_resource.py @@ -0,0 +1,260 @@ +import sys +import unittest +import importlib_resources as resources +import uuid +import pathlib + +from . import data01 +from . import zipdata01, zipdata02 +from . import util +from importlib import import_module +from ._compat import import_helper, unlink + + +class ResourceTests: + # Subclasses are expected to set the `data` attribute. + + def test_is_file_exists(self): + target = resources.files(self.data) / 'binary.file' + self.assertTrue(target.is_file()) + + def test_is_file_missing(self): + target = resources.files(self.data) / 'not-a-file' + self.assertFalse(target.is_file()) + + def test_is_dir(self): + target = resources.files(self.data) / 'subdirectory' + self.assertFalse(target.is_file()) + self.assertTrue(target.is_dir()) + + +class ResourceDiskTests(ResourceTests, unittest.TestCase): + def setUp(self): + self.data = data01 + + +class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase): + pass + + +def names(traversable): + return {item.name for item in traversable.iterdir()} + + +class ResourceLoaderTests(unittest.TestCase): + def test_resource_contents(self): + package = util.create_package( + file=data01, path=data01.__file__, contents=['A', 'B', 'C'] + ) + self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'}) + + def test_is_file(self): + package = util.create_package( + file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'] + ) + self.assertTrue(resources.files(package).joinpath('B').is_file()) + + def test_is_dir(self): + package = util.create_package( + file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'] + ) + self.assertTrue(resources.files(package).joinpath('D').is_dir()) + + def test_resource_missing(self): + package = util.create_package( + file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F'] + ) + self.assertFalse(resources.files(package).joinpath('Z').is_file()) + + +class ResourceCornerCaseTests(unittest.TestCase): + def test_package_has_no_reader_fallback(self): + # Test odd ball packages which: + # 1. Do not have a ResourceReader as a loader + # 2. Are not on the file system + # 3. Are not in a zip file + module = util.create_package( + file=data01, path=data01.__file__, contents=['A', 'B', 'C'] + ) + # Give the module a dummy loader. + module.__loader__ = object() + # Give the module a dummy origin. + module.__file__ = '/path/which/shall/not/be/named' + module.__spec__.loader = module.__loader__ + module.__spec__.origin = module.__file__ + self.assertFalse(resources.files(module).joinpath('A').is_file()) + + +class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase): + ZIP_MODULE = zipdata01 # type: ignore + + def test_is_submodule_resource(self): + submodule = import_module('ziptestdata.subdirectory') + self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file()) + + def test_read_submodule_resource_by_name(self): + self.assertTrue( + resources.files('ziptestdata.subdirectory') + .joinpath('binary.file') + .is_file() + ) + + def test_submodule_contents(self): + submodule = import_module('ziptestdata.subdirectory') + self.assertEqual( + names(resources.files(submodule)), {'__init__.py', 'binary.file'} + ) + + def test_submodule_contents_by_name(self): + self.assertEqual( + names(resources.files('ziptestdata.subdirectory')), + {'__init__.py', 'binary.file'}, + ) + + def test_as_file_directory(self): + with resources.as_file(resources.files('ziptestdata')) as data: + assert data.name == 'ziptestdata' + assert data.is_dir() + assert data.joinpath('subdirectory').is_dir() + assert len(list(data.iterdir())) + assert not data.parent.exists() + + +class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase): + ZIP_MODULE = zipdata02 # type: ignore + + def test_unrelated_contents(self): + """ + Test thata zip with two unrelated subpackages return + distinct resources. Ref python/importlib_resources#44. + """ + self.assertEqual( + names(resources.files('ziptestdata.one')), + {'__init__.py', 'resource1.txt'}, + ) + self.assertEqual( + names(resources.files('ziptestdata.two')), + {'__init__.py', 'resource2.txt'}, + ) + + +class DeletingZipsTest(unittest.TestCase): + """Having accessed resources in a zip file should not keep an open + reference to the zip. + """ + + ZIP_MODULE = zipdata01 + + def setUp(self): + modules = import_helper.modules_setup() + self.addCleanup(import_helper.modules_cleanup, *modules) + + data_path = pathlib.Path(self.ZIP_MODULE.__file__) + data_dir = data_path.parent + self.source_zip_path = data_dir / 'ziptestdata.zip' + self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute() + self.zip_path.write_bytes(self.source_zip_path.read_bytes()) + sys.path.append(str(self.zip_path)) + self.data = import_module('ziptestdata') + + def tearDown(self): + try: + sys.path.remove(str(self.zip_path)) + except ValueError: + pass + + try: + del sys.path_importer_cache[str(self.zip_path)] + del sys.modules[self.data.__name__] + except KeyError: + pass + + try: + unlink(self.zip_path) + except OSError: + # If the test fails, this will probably fail too + pass + + def test_iterdir_does_not_keep_open(self): + c = [item.name for item in resources.files('ziptestdata').iterdir()] + self.zip_path.unlink() + del c + + def test_is_file_does_not_keep_open(self): + c = resources.files('ziptestdata').joinpath('binary.file').is_file() + self.zip_path.unlink() + del c + + def test_is_file_failure_does_not_keep_open(self): + c = resources.files('ziptestdata').joinpath('not-present').is_file() + self.zip_path.unlink() + del c + + @unittest.skip("Desired but not supported.") + def test_as_file_does_not_keep_open(self): # pragma: no cover + c = resources.as_file(resources.files('ziptestdata') / 'binary.file') + self.zip_path.unlink() + del c + + def test_entered_path_does_not_keep_open(self): + # This is what certifi does on import to make its bundle + # available for the process duration. + c = resources.as_file( + resources.files('ziptestdata') / 'binary.file' + ).__enter__() + self.zip_path.unlink() + del c + + def test_read_binary_does_not_keep_open(self): + c = resources.files('ziptestdata').joinpath('binary.file').read_bytes() + self.zip_path.unlink() + del c + + def test_read_text_does_not_keep_open(self): + c = resources.files('ziptestdata').joinpath('utf-8.file').read_text() + self.zip_path.unlink() + del c + + +class ResourceFromNamespaceTest01(unittest.TestCase): + site_dir = str(pathlib.Path(__file__).parent) + + @classmethod + def setUpClass(cls): + sys.path.append(cls.site_dir) + + @classmethod + def tearDownClass(cls): + sys.path.remove(cls.site_dir) + + def test_is_submodule_resource(self): + self.assertTrue( + resources.files(import_module('namespacedata01')) + .joinpath('binary.file') + .is_file() + ) + + def test_read_submodule_resource_by_name(self): + self.assertTrue( + resources.files('namespacedata01').joinpath('binary.file').is_file() + ) + + def test_submodule_contents(self): + contents = names(resources.files(import_module('namespacedata01'))) + try: + contents.remove('__pycache__') + except KeyError: + pass + self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'}) + + def test_submodule_contents_by_name(self): + contents = names(resources.files('namespacedata01')) + try: + contents.remove('__pycache__') + except KeyError: + pass + self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'}) + + +if __name__ == '__main__': + unittest.main() diff --git a/libs/win/importlib_resources/tests/update-zips.py b/libs/win/importlib_resources/tests/update-zips.py new file mode 100644 index 00000000..231334aa --- /dev/null +++ b/libs/win/importlib_resources/tests/update-zips.py @@ -0,0 +1,53 @@ +""" +Generate the zip test data files. + +Run to build the tests/zipdataNN/ziptestdata.zip files from +files in tests/dataNN. + +Replaces the file with the working copy, but does commit anything +to the source repo. +""" + +import contextlib +import os +import pathlib +import zipfile + + +def main(): + """ + >>> from unittest import mock + >>> monkeypatch = getfixture('monkeypatch') + >>> monkeypatch.setattr(zipfile, 'ZipFile', mock.MagicMock()) + >>> print(); main() # print workaround for bpo-32509 + + ...data01... -> ziptestdata/... + ... + ...data02... -> ziptestdata/... + ... + """ + suffixes = '01', '02' + tuple(map(generate, suffixes)) + + +def generate(suffix): + root = pathlib.Path(__file__).parent.relative_to(os.getcwd()) + zfpath = root / f'zipdata{suffix}/ziptestdata.zip' + with zipfile.ZipFile(zfpath, 'w') as zf: + for src, rel in walk(root / f'data{suffix}'): + dst = 'ziptestdata' / pathlib.PurePosixPath(rel.as_posix()) + print(src, '->', dst) + zf.write(src, dst) + + +def walk(datapath): + for dirpath, dirnames, filenames in os.walk(datapath): + with contextlib.suppress(ValueError): + dirnames.remove('__pycache__') + for filename in filenames: + res = pathlib.Path(dirpath) / filename + rel = res.relative_to(datapath) + yield res, rel + + +__name__ == '__main__' and main() diff --git a/libs/win/importlib_resources/tests/util.py b/libs/win/importlib_resources/tests/util.py new file mode 100644 index 00000000..b596c0ce --- /dev/null +++ b/libs/win/importlib_resources/tests/util.py @@ -0,0 +1,167 @@ +import abc +import importlib +import io +import sys +import types +import pathlib + +from . import data01 +from . import zipdata01 +from ..abc import ResourceReader +from ._compat import import_helper + + +from importlib.machinery import ModuleSpec + + +class Reader(ResourceReader): + def __init__(self, **kwargs): + vars(self).update(kwargs) + + def get_resource_reader(self, package): + return self + + def open_resource(self, path): + self._path = path + if isinstance(self.file, Exception): + raise self.file + return self.file + + def resource_path(self, path_): + self._path = path_ + if isinstance(self.path, Exception): + raise self.path + return self.path + + def is_resource(self, path_): + self._path = path_ + if isinstance(self.path, Exception): + raise self.path + + def part(entry): + return entry.split('/') + + return any( + len(parts) == 1 and parts[0] == path_ for parts in map(part, self._contents) + ) + + def contents(self): + if isinstance(self.path, Exception): + raise self.path + yield from self._contents + + +def create_package_from_loader(loader, is_package=True): + name = 'testingpackage' + module = types.ModuleType(name) + spec = ModuleSpec(name, loader, origin='does-not-exist', is_package=is_package) + module.__spec__ = spec + module.__loader__ = loader + return module + + +def create_package(file=None, path=None, is_package=True, contents=()): + return create_package_from_loader( + Reader(file=file, path=path, _contents=contents), + is_package, + ) + + +class CommonTests(metaclass=abc.ABCMeta): + """ + Tests shared by test_open, test_path, and test_read. + """ + + @abc.abstractmethod + def execute(self, package, path): + """ + Call the pertinent legacy API function (e.g. open_text, path) + on package and path. + """ + + def test_package_name(self): + # Passing in the package name should succeed. + self.execute(data01.__name__, 'utf-8.file') + + def test_package_object(self): + # Passing in the package itself should succeed. + self.execute(data01, 'utf-8.file') + + def test_string_path(self): + # Passing in a string for the path should succeed. + path = 'utf-8.file' + self.execute(data01, path) + + def test_pathlib_path(self): + # Passing in a pathlib.PurePath object for the path should succeed. + path = pathlib.PurePath('utf-8.file') + self.execute(data01, path) + + def test_importing_module_as_side_effect(self): + # The anchor package can already be imported. + del sys.modules[data01.__name__] + self.execute(data01.__name__, 'utf-8.file') + + def test_missing_path(self): + # Attempting to open or read or request the path for a + # non-existent path should succeed if open_resource + # can return a viable data stream. + bytes_data = io.BytesIO(b'Hello, world!') + package = create_package(file=bytes_data, path=FileNotFoundError()) + self.execute(package, 'utf-8.file') + self.assertEqual(package.__loader__._path, 'utf-8.file') + + def test_extant_path(self): + # Attempting to open or read or request the path when the + # path does exist should still succeed. Does not assert + # anything about the result. + bytes_data = io.BytesIO(b'Hello, world!') + # any path that exists + path = __file__ + package = create_package(file=bytes_data, path=path) + self.execute(package, 'utf-8.file') + self.assertEqual(package.__loader__._path, 'utf-8.file') + + def test_useless_loader(self): + package = create_package(file=FileNotFoundError(), path=FileNotFoundError()) + with self.assertRaises(FileNotFoundError): + self.execute(package, 'utf-8.file') + + +class ZipSetupBase: + ZIP_MODULE = None + + @classmethod + def setUpClass(cls): + data_path = pathlib.Path(cls.ZIP_MODULE.__file__) + data_dir = data_path.parent + cls._zip_path = str(data_dir / 'ziptestdata.zip') + sys.path.append(cls._zip_path) + cls.data = importlib.import_module('ziptestdata') + + @classmethod + def tearDownClass(cls): + try: + sys.path.remove(cls._zip_path) + except ValueError: + pass + + try: + del sys.path_importer_cache[cls._zip_path] + del sys.modules[cls.data.__name__] + except KeyError: + pass + + try: + del cls.data + del cls._zip_path + except AttributeError: + pass + + def setUp(self): + modules = import_helper.modules_setup() + self.addCleanup(import_helper.modules_cleanup, *modules) + + +class ZipSetup(ZipSetupBase): + ZIP_MODULE = zipdata01 # type: ignore diff --git a/libs/win/importlib_resources/tests/zipdata01/__init__.py b/libs/win/importlib_resources/tests/zipdata01/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/importlib_resources/tests/zipdata01/ziptestdata.zip b/libs/win/importlib_resources/tests/zipdata01/ziptestdata.zip new file mode 100644 index 0000000000000000000000000000000000000000..9a3bb0739f87e97c1084b94d7d153680f6727738 GIT binary patch literal 876 zcmWIWW@Zs#00HOCX@Q%&m27l?Y!DU);;PJolGNgol*E!m{nC;&T|+ayw9K5;|NlG~ zQWMD z9;rDw`8o=rA#S=B3g!7lIVp-}COK17UPc zNtt;*xhM-3R!jMEPhCreO-3*u>5Df}T7+BJ{639e$2uhfsIs`pJ5Qf}C xGXyDE@VNvOv@o!wQJfLgCAgysx3f@9jKpUmiW^zkK<;1z!tFpk^MROw0RS~O%0&PG literal 0 HcmV?d00001 diff --git a/libs/win/importlib_resources/tests/zipdata02/__init__.py b/libs/win/importlib_resources/tests/zipdata02/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/importlib_resources/tests/zipdata02/ziptestdata.zip b/libs/win/importlib_resources/tests/zipdata02/ziptestdata.zip new file mode 100644 index 0000000000000000000000000000000000000000..d63ff512d2807ef2fd259455283b81b02e0e45fb GIT binary patch literal 698 zcmWIWW@Zs#00HOCX@Ot{ln@8fRhb1Psl_EJi6x2p@$s2?nI-Y@dIgmMI5kP5Y0A$_ z#jWw|&p#`9ff_(q7K_HB)Z+ZoqU2OVy^@L&ph*fa0WRVlP*R?c+X1opI-R&20MZDv z&j{oIpa8N17@0(vaR(gGH(;=&5k%n(M%;#g0ulz6G@1gL$cA79E2=^00gEsw4~s!C zUxI@ZWaIMqz|BszK;s4KsL2<9jRy!Q2E6`2cTLHjr{wAk1ZCU@!+_ G1_l6Bc%f?m literal 0 HcmV?d00001 diff --git a/libs/win/incubator/replace-file.py b/libs/win/incubator/replace-file.py new file mode 100644 index 00000000..a4d24bde --- /dev/null +++ b/libs/win/incubator/replace-file.py @@ -0,0 +1,10 @@ +from jaraco.windows.api.filesystem import ReplaceFile + +open('orig-file', 'w').write('some content') +open('replacing-file', 'w').write('new content') +ReplaceFile('orig-file', 'replacing-file', 'orig-backup', 0, 0, 0) +assert open('orig-file').read() == 'new content' +assert open('orig-backup').read() == 'some content' +import os + +assert not os.path.exists('replacing-file') diff --git a/libs/win/incubator/trace-symlink.py b/libs/win/incubator/trace-symlink.py new file mode 100644 index 00000000..1e7716db --- /dev/null +++ b/libs/win/incubator/trace-symlink.py @@ -0,0 +1,22 @@ +from jaraco.windows.filesystem import trace_symlink_target + +from optparse import OptionParser + + +def get_args(): + parser = OptionParser() + options, args = parser.parse_args() + try: + options.filename = args.pop(0) + except IndexError: + parser.error('filename required') + return options + + +def main(): + options = get_args() + print(trace_symlink_target(options.filename)) + + +if __name__ == '__main__': + main() diff --git a/libs/win/inflect/__init__.py b/libs/win/inflect/__init__.py new file mode 100644 index 00000000..78d2e33c --- /dev/null +++ b/libs/win/inflect/__init__.py @@ -0,0 +1,3991 @@ +""" +inflect: english language inflection + - correctly generate plurals, ordinals, indefinite articles + - convert numbers to words + +Copyright (C) 2010 Paul Dyson + +Based upon the Perl module +`Lingua::EN::Inflect `_. + +methods: + classical inflect + plural plural_noun plural_verb plural_adj singular_noun no num a an + compare compare_nouns compare_verbs compare_adjs + present_participle + ordinal + number_to_words + join + defnoun defverb defadj defa defan + +INFLECTIONS: + classical inflect + plural plural_noun plural_verb plural_adj singular_noun compare + no num a an present_participle + +PLURALS: + classical inflect + plural plural_noun plural_verb plural_adj singular_noun no num + compare compare_nouns compare_verbs compare_adjs + +COMPARISONS: + classical + compare compare_nouns compare_verbs compare_adjs + +ARTICLES: + classical inflect num a an + +NUMERICAL: + ordinal number_to_words + +USER_DEFINED: + defnoun defverb defadj defa defan + +Exceptions: + UnknownClassicalModeError + BadNumValueError + BadChunkingOptionError + NumOutOfRangeError + BadUserDefinedPatternError + BadRcFileError + BadGenderError + +""" + +import ast +import re +import functools +import collections +import contextlib +from typing import ( + Dict, + Union, + Optional, + Iterable, + List, + Match, + Tuple, + Callable, + Sequence, + cast, + Any, +) +from numbers import Number + + +from pydantic import Field, validate_arguments +from pydantic.typing import Annotated + + +class UnknownClassicalModeError(Exception): + pass + + +class BadNumValueError(Exception): + pass + + +class BadChunkingOptionError(Exception): + pass + + +class NumOutOfRangeError(Exception): + pass + + +class BadUserDefinedPatternError(Exception): + pass + + +class BadRcFileError(Exception): + pass + + +class BadGenderError(Exception): + pass + + +STDOUT_ON = False + + +def print3(txt: str) -> None: + if STDOUT_ON: + print(txt) + + +def enclose(s: str) -> str: + return f"(?:{s})" + + +def joinstem(cutpoint: Optional[int] = 0, words: Optional[Iterable[str]] = None) -> str: + """ + Join stem of each word in words into a string for regex. + + Each word is truncated at cutpoint. + + Cutpoint is usually negative indicating the number of letters to remove + from the end of each word. + + >>> joinstem(-2, ["ephemeris", "iris", ".*itis"]) + '(?:ephemer|ir|.*it)' + + >>> joinstem(None, ["ephemeris"]) + '(?:ephemeris)' + + >>> joinstem(5, None) + '(?:)' + """ + return enclose("|".join(w[:cutpoint] for w in words or [])) + + +def bysize(words: Iterable[str]) -> Dict[int, set]: + """ + From a list of words, return a dict of sets sorted by word length. + + >>> words = ['ant', 'cat', 'dog', 'pig', 'frog', 'goat', 'horse', 'elephant'] + >>> ret = bysize(words) + >>> sorted(ret[3]) + ['ant', 'cat', 'dog', 'pig'] + >>> ret[5] + {'horse'} + """ + res: Dict[int, set] = collections.defaultdict(set) + for w in words: + res[len(w)].add(w) + return res + + +def make_pl_si_lists( + lst: Iterable[str], + plending: str, + siendingsize: Optional[int], + dojoinstem: bool = True, +): + """ + given a list of singular words: lst + + an ending to append to make the plural: plending + + the number of characters to remove from the singular + before appending plending: siendingsize + + a flag whether to create a joinstem: dojoinstem + + return: + a list of pluralised words: si_list (called si because this is what you need to + look for to make the singular) + + the pluralised words as a dict of sets sorted by word length: si_bysize + the singular words as a dict of sets sorted by word length: pl_bysize + if dojoinstem is True: a regular expression that matches any of the stems: stem + """ + if siendingsize is not None: + siendingsize = -siendingsize + si_list = [w[:siendingsize] + plending for w in lst] + pl_bysize = bysize(lst) + si_bysize = bysize(si_list) + if dojoinstem: + stem = joinstem(siendingsize, lst) + return si_list, si_bysize, pl_bysize, stem + else: + return si_list, si_bysize, pl_bysize + + +# 1. PLURALS + +pl_sb_irregular_s = { + "corpus": "corpuses|corpora", + "opus": "opuses|opera", + "genus": "genera", + "mythos": "mythoi", + "penis": "penises|penes", + "testis": "testes", + "atlas": "atlases|atlantes", + "yes": "yeses", +} + +pl_sb_irregular = { + "child": "children", + "chili": "chilis|chilies", + "brother": "brothers|brethren", + "infinity": "infinities|infinity", + "loaf": "loaves", + "lore": "lores|lore", + "hoof": "hoofs|hooves", + "beef": "beefs|beeves", + "thief": "thiefs|thieves", + "money": "monies", + "mongoose": "mongooses", + "ox": "oxen", + "cow": "cows|kine", + "graffito": "graffiti", + "octopus": "octopuses|octopodes", + "genie": "genies|genii", + "ganglion": "ganglions|ganglia", + "trilby": "trilbys", + "turf": "turfs|turves", + "numen": "numina", + "atman": "atmas", + "occiput": "occiputs|occipita", + "sabretooth": "sabretooths", + "sabertooth": "sabertooths", + "lowlife": "lowlifes", + "flatfoot": "flatfoots", + "tenderfoot": "tenderfoots", + "romany": "romanies", + "jerry": "jerries", + "mary": "maries", + "talouse": "talouses", + "rom": "roma", + "carmen": "carmina", +} + +pl_sb_irregular.update(pl_sb_irregular_s) +# pl_sb_irregular_keys = enclose('|'.join(pl_sb_irregular.keys())) + +pl_sb_irregular_caps = { + "Romany": "Romanies", + "Jerry": "Jerrys", + "Mary": "Marys", + "Rom": "Roma", +} + +pl_sb_irregular_compound = {"prima donna": "prima donnas|prime donne"} + +si_sb_irregular = {v: k for (k, v) in pl_sb_irregular.items()} +for k in list(si_sb_irregular): + if "|" in k: + k1, k2 = k.split("|") + si_sb_irregular[k1] = si_sb_irregular[k2] = si_sb_irregular[k] + del si_sb_irregular[k] +si_sb_irregular_caps = {v: k for (k, v) in pl_sb_irregular_caps.items()} +si_sb_irregular_compound = {v: k for (k, v) in pl_sb_irregular_compound.items()} +for k in list(si_sb_irregular_compound): + if "|" in k: + k1, k2 = k.split("|") + si_sb_irregular_compound[k1] = si_sb_irregular_compound[ + k2 + ] = si_sb_irregular_compound[k] + del si_sb_irregular_compound[k] + +# si_sb_irregular_keys = enclose('|'.join(si_sb_irregular.keys())) + +# Z's that don't double + +pl_sb_z_zes_list = ("quartz", "topaz") +pl_sb_z_zes_bysize = bysize(pl_sb_z_zes_list) + +pl_sb_ze_zes_list = ("snooze",) +pl_sb_ze_zes_bysize = bysize(pl_sb_ze_zes_list) + + +# CLASSICAL "..is" -> "..ides" + +pl_sb_C_is_ides_complete = [ + # GENERAL WORDS... + "ephemeris", + "iris", + "clitoris", + "chrysalis", + "epididymis", +] + +pl_sb_C_is_ides_endings = [ + # INFLAMATIONS... + "itis" +] + +pl_sb_C_is_ides = joinstem( + -2, pl_sb_C_is_ides_complete + [f".*{w}" for w in pl_sb_C_is_ides_endings] +) + +pl_sb_C_is_ides_list = pl_sb_C_is_ides_complete + pl_sb_C_is_ides_endings + +( + si_sb_C_is_ides_list, + si_sb_C_is_ides_bysize, + pl_sb_C_is_ides_bysize, +) = make_pl_si_lists(pl_sb_C_is_ides_list, "ides", 2, dojoinstem=False) + + +# CLASSICAL "..a" -> "..ata" + +pl_sb_C_a_ata_list = ( + "anathema", + "bema", + "carcinoma", + "charisma", + "diploma", + "dogma", + "drama", + "edema", + "enema", + "enigma", + "lemma", + "lymphoma", + "magma", + "melisma", + "miasma", + "oedema", + "sarcoma", + "schema", + "soma", + "stigma", + "stoma", + "trauma", + "gumma", + "pragma", +) + +( + si_sb_C_a_ata_list, + si_sb_C_a_ata_bysize, + pl_sb_C_a_ata_bysize, + pl_sb_C_a_ata, +) = make_pl_si_lists(pl_sb_C_a_ata_list, "ata", 1) + +# UNCONDITIONAL "..a" -> "..ae" + +pl_sb_U_a_ae_list = ( + "alumna", + "alga", + "vertebra", + "persona", + "vita", +) +( + si_sb_U_a_ae_list, + si_sb_U_a_ae_bysize, + pl_sb_U_a_ae_bysize, + pl_sb_U_a_ae, +) = make_pl_si_lists(pl_sb_U_a_ae_list, "e", None) + +# CLASSICAL "..a" -> "..ae" + +pl_sb_C_a_ae_list = ( + "amoeba", + "antenna", + "formula", + "hyperbola", + "medusa", + "nebula", + "parabola", + "abscissa", + "hydra", + "nova", + "lacuna", + "aurora", + "umbra", + "flora", + "fauna", +) +( + si_sb_C_a_ae_list, + si_sb_C_a_ae_bysize, + pl_sb_C_a_ae_bysize, + pl_sb_C_a_ae, +) = make_pl_si_lists(pl_sb_C_a_ae_list, "e", None) + + +# CLASSICAL "..en" -> "..ina" + +pl_sb_C_en_ina_list = ("stamen", "foramen", "lumen") + +( + si_sb_C_en_ina_list, + si_sb_C_en_ina_bysize, + pl_sb_C_en_ina_bysize, + pl_sb_C_en_ina, +) = make_pl_si_lists(pl_sb_C_en_ina_list, "ina", 2) + + +# UNCONDITIONAL "..um" -> "..a" + +pl_sb_U_um_a_list = ( + "bacterium", + "agendum", + "desideratum", + "erratum", + "stratum", + "datum", + "ovum", + "extremum", + "candelabrum", +) +( + si_sb_U_um_a_list, + si_sb_U_um_a_bysize, + pl_sb_U_um_a_bysize, + pl_sb_U_um_a, +) = make_pl_si_lists(pl_sb_U_um_a_list, "a", 2) + +# CLASSICAL "..um" -> "..a" + +pl_sb_C_um_a_list = ( + "maximum", + "minimum", + "momentum", + "optimum", + "quantum", + "cranium", + "curriculum", + "dictum", + "phylum", + "aquarium", + "compendium", + "emporium", + "encomium", + "gymnasium", + "honorarium", + "interregnum", + "lustrum", + "memorandum", + "millennium", + "rostrum", + "spectrum", + "speculum", + "stadium", + "trapezium", + "ultimatum", + "medium", + "vacuum", + "velum", + "consortium", + "arboretum", +) + +( + si_sb_C_um_a_list, + si_sb_C_um_a_bysize, + pl_sb_C_um_a_bysize, + pl_sb_C_um_a, +) = make_pl_si_lists(pl_sb_C_um_a_list, "a", 2) + + +# UNCONDITIONAL "..us" -> "i" + +pl_sb_U_us_i_list = ( + "alumnus", + "alveolus", + "bacillus", + "bronchus", + "locus", + "nucleus", + "stimulus", + "meniscus", + "sarcophagus", +) +( + si_sb_U_us_i_list, + si_sb_U_us_i_bysize, + pl_sb_U_us_i_bysize, + pl_sb_U_us_i, +) = make_pl_si_lists(pl_sb_U_us_i_list, "i", 2) + +# CLASSICAL "..us" -> "..i" + +pl_sb_C_us_i_list = ( + "focus", + "radius", + "genius", + "incubus", + "succubus", + "nimbus", + "fungus", + "nucleolus", + "stylus", + "torus", + "umbilicus", + "uterus", + "hippopotamus", + "cactus", +) + +( + si_sb_C_us_i_list, + si_sb_C_us_i_bysize, + pl_sb_C_us_i_bysize, + pl_sb_C_us_i, +) = make_pl_si_lists(pl_sb_C_us_i_list, "i", 2) + + +# CLASSICAL "..us" -> "..us" (ASSIMILATED 4TH DECLENSION LATIN NOUNS) + +pl_sb_C_us_us = ( + "status", + "apparatus", + "prospectus", + "sinus", + "hiatus", + "impetus", + "plexus", +) +pl_sb_C_us_us_bysize = bysize(pl_sb_C_us_us) + +# UNCONDITIONAL "..on" -> "a" + +pl_sb_U_on_a_list = ( + "criterion", + "perihelion", + "aphelion", + "phenomenon", + "prolegomenon", + "noumenon", + "organon", + "asyndeton", + "hyperbaton", +) +( + si_sb_U_on_a_list, + si_sb_U_on_a_bysize, + pl_sb_U_on_a_bysize, + pl_sb_U_on_a, +) = make_pl_si_lists(pl_sb_U_on_a_list, "a", 2) + +# CLASSICAL "..on" -> "..a" + +pl_sb_C_on_a_list = ("oxymoron",) + +( + si_sb_C_on_a_list, + si_sb_C_on_a_bysize, + pl_sb_C_on_a_bysize, + pl_sb_C_on_a, +) = make_pl_si_lists(pl_sb_C_on_a_list, "a", 2) + + +# CLASSICAL "..o" -> "..i" (BUT NORMALLY -> "..os") + +pl_sb_C_o_i = [ + "solo", + "soprano", + "basso", + "alto", + "contralto", + "tempo", + "piano", + "virtuoso", +] # list not tuple so can concat for pl_sb_U_o_os + +pl_sb_C_o_i_bysize = bysize(pl_sb_C_o_i) +si_sb_C_o_i_bysize = bysize([f"{w[:-1]}i" for w in pl_sb_C_o_i]) + +pl_sb_C_o_i_stems = joinstem(-1, pl_sb_C_o_i) + +# ALWAYS "..o" -> "..os" + +pl_sb_U_o_os_complete = {"ado", "ISO", "NATO", "NCO", "NGO", "oto"} +si_sb_U_o_os_complete = {f"{w}s" for w in pl_sb_U_o_os_complete} + + +pl_sb_U_o_os_endings = [ + "aficionado", + "aggro", + "albino", + "allegro", + "ammo", + "Antananarivo", + "archipelago", + "armadillo", + "auto", + "avocado", + "Bamako", + "Barquisimeto", + "bimbo", + "bingo", + "Biro", + "bolero", + "Bolzano", + "bongo", + "Boto", + "burro", + "Cairo", + "canto", + "cappuccino", + "casino", + "cello", + "Chicago", + "Chimango", + "cilantro", + "cochito", + "coco", + "Colombo", + "Colorado", + "commando", + "concertino", + "contango", + "credo", + "crescendo", + "cyano", + "demo", + "ditto", + "Draco", + "dynamo", + "embryo", + "Esperanto", + "espresso", + "euro", + "falsetto", + "Faro", + "fiasco", + "Filipino", + "flamenco", + "furioso", + "generalissimo", + "Gestapo", + "ghetto", + "gigolo", + "gizmo", + "Greensboro", + "gringo", + "Guaiabero", + "guano", + "gumbo", + "gyro", + "hairdo", + "hippo", + "Idaho", + "impetigo", + "inferno", + "info", + "intermezzo", + "intertrigo", + "Iquico", + "jumbo", + "junto", + "Kakapo", + "kilo", + "Kinkimavo", + "Kokako", + "Kosovo", + "Lesotho", + "libero", + "libido", + "libretto", + "lido", + "Lilo", + "limbo", + "limo", + "lineno", + "lingo", + "lino", + "livedo", + "loco", + "logo", + "lumbago", + "macho", + "macro", + "mafioso", + "magneto", + "magnifico", + "Majuro", + "Malabo", + "manifesto", + "Maputo", + "Maracaibo", + "medico", + "memo", + "metro", + "Mexico", + "micro", + "Milano", + "Monaco", + "mono", + "Montenegro", + "Morocco", + "Muqdisho", + "myo", + "neutrino", + "Ningbo", + "octavo", + "oregano", + "Orinoco", + "Orlando", + "Oslo", + "panto", + "Paramaribo", + "Pardusco", + "pedalo", + "photo", + "pimento", + "pinto", + "pleco", + "Pluto", + "pogo", + "polo", + "poncho", + "Porto-Novo", + "Porto", + "pro", + "psycho", + "pueblo", + "quarto", + "Quito", + "repo", + "rhino", + "risotto", + "rococo", + "rondo", + "Sacramento", + "saddo", + "sago", + "salvo", + "Santiago", + "Sapporo", + "Sarajevo", + "scherzando", + "scherzo", + "silo", + "sirocco", + "sombrero", + "staccato", + "sterno", + "stucco", + "stylo", + "sumo", + "Taiko", + "techno", + "terrazzo", + "testudo", + "timpano", + "tiro", + "tobacco", + "Togo", + "Tokyo", + "torero", + "Torino", + "Toronto", + "torso", + "tremolo", + "typo", + "tyro", + "ufo", + "UNESCO", + "vaquero", + "vermicello", + "verso", + "vibrato", + "violoncello", + "Virgo", + "weirdo", + "WHO", + "WTO", + "Yamoussoukro", + "yo-yo", + "zero", + "Zibo", +] + pl_sb_C_o_i + +pl_sb_U_o_os_bysize = bysize(pl_sb_U_o_os_endings) +si_sb_U_o_os_bysize = bysize([f"{w}s" for w in pl_sb_U_o_os_endings]) + + +# UNCONDITIONAL "..ch" -> "..chs" + +pl_sb_U_ch_chs_list = ("czech", "eunuch", "stomach") + +( + si_sb_U_ch_chs_list, + si_sb_U_ch_chs_bysize, + pl_sb_U_ch_chs_bysize, + pl_sb_U_ch_chs, +) = make_pl_si_lists(pl_sb_U_ch_chs_list, "s", None) + + +# UNCONDITIONAL "..[ei]x" -> "..ices" + +pl_sb_U_ex_ices_list = ("codex", "murex", "silex") +( + si_sb_U_ex_ices_list, + si_sb_U_ex_ices_bysize, + pl_sb_U_ex_ices_bysize, + pl_sb_U_ex_ices, +) = make_pl_si_lists(pl_sb_U_ex_ices_list, "ices", 2) + +pl_sb_U_ix_ices_list = ("radix", "helix") +( + si_sb_U_ix_ices_list, + si_sb_U_ix_ices_bysize, + pl_sb_U_ix_ices_bysize, + pl_sb_U_ix_ices, +) = make_pl_si_lists(pl_sb_U_ix_ices_list, "ices", 2) + +# CLASSICAL "..[ei]x" -> "..ices" + +pl_sb_C_ex_ices_list = ( + "vortex", + "vertex", + "cortex", + "latex", + "pontifex", + "apex", + "index", + "simplex", +) + +( + si_sb_C_ex_ices_list, + si_sb_C_ex_ices_bysize, + pl_sb_C_ex_ices_bysize, + pl_sb_C_ex_ices, +) = make_pl_si_lists(pl_sb_C_ex_ices_list, "ices", 2) + + +pl_sb_C_ix_ices_list = ("appendix",) + +( + si_sb_C_ix_ices_list, + si_sb_C_ix_ices_bysize, + pl_sb_C_ix_ices_bysize, + pl_sb_C_ix_ices, +) = make_pl_si_lists(pl_sb_C_ix_ices_list, "ices", 2) + + +# ARABIC: ".." -> "..i" + +pl_sb_C_i_list = ("afrit", "afreet", "efreet") + +(si_sb_C_i_list, si_sb_C_i_bysize, pl_sb_C_i_bysize, pl_sb_C_i) = make_pl_si_lists( + pl_sb_C_i_list, "i", None +) + + +# HEBREW: ".." -> "..im" + +pl_sb_C_im_list = ("goy", "seraph", "cherub") + +(si_sb_C_im_list, si_sb_C_im_bysize, pl_sb_C_im_bysize, pl_sb_C_im) = make_pl_si_lists( + pl_sb_C_im_list, "im", None +) + + +# UNCONDITIONAL "..man" -> "..mans" + +pl_sb_U_man_mans_list = """ + ataman caiman cayman ceriman + desman dolman farman harman hetman + human leman ottoman shaman talisman +""".split() +pl_sb_U_man_mans_caps_list = """ + Alabaman Bahaman Burman German + Hiroshiman Liman Nakayaman Norman Oklahoman + Panaman Roman Selman Sonaman Tacoman Yakiman + Yokohaman Yuman +""".split() + +( + si_sb_U_man_mans_list, + si_sb_U_man_mans_bysize, + pl_sb_U_man_mans_bysize, +) = make_pl_si_lists(pl_sb_U_man_mans_list, "s", None, dojoinstem=False) +( + si_sb_U_man_mans_caps_list, + si_sb_U_man_mans_caps_bysize, + pl_sb_U_man_mans_caps_bysize, +) = make_pl_si_lists(pl_sb_U_man_mans_caps_list, "s", None, dojoinstem=False) + +# UNCONDITIONAL "..louse" -> "..lice" +pl_sb_U_louse_lice_list = ("booklouse", "grapelouse", "louse", "woodlouse") + +( + si_sb_U_louse_lice_list, + si_sb_U_louse_lice_bysize, + pl_sb_U_louse_lice_bysize, +) = make_pl_si_lists(pl_sb_U_louse_lice_list, "lice", 5, dojoinstem=False) + +pl_sb_uninflected_s_complete = [ + # PAIRS OR GROUPS SUBSUMED TO A SINGULAR... + "breeches", + "britches", + "pajamas", + "pyjamas", + "clippers", + "gallows", + "hijinks", + "headquarters", + "pliers", + "scissors", + "testes", + "herpes", + "pincers", + "shears", + "proceedings", + "trousers", + # UNASSIMILATED LATIN 4th DECLENSION + "cantus", + "coitus", + "nexus", + # RECENT IMPORTS... + "contretemps", + "corps", + "debris", + "siemens", + # DISEASES + "mumps", + # MISCELLANEOUS OTHERS... + "diabetes", + "jackanapes", + "series", + "species", + "subspecies", + "rabies", + "chassis", + "innings", + "news", + "mews", + "haggis", +] + +pl_sb_uninflected_s_endings = [ + # RECENT IMPORTS... + "ois", + # DISEASES + "measles", +] + +pl_sb_uninflected_s = pl_sb_uninflected_s_complete + [ + f".*{w}" for w in pl_sb_uninflected_s_endings +] + +pl_sb_uninflected_herd = ( + # DON'T INFLECT IN CLASSICAL MODE, OTHERWISE NORMAL INFLECTION + "wildebeest", + "swine", + "eland", + "bison", + "buffalo", + "cattle", + "elk", + "rhinoceros", + "zucchini", + "caribou", + "dace", + "grouse", + "guinea fowl", + "guinea-fowl", + "haddock", + "hake", + "halibut", + "herring", + "mackerel", + "pickerel", + "pike", + "roe", + "seed", + "shad", + "snipe", + "teal", + "turbot", + "water fowl", + "water-fowl", +) + +pl_sb_uninflected_complete = [ + # SOME FISH AND HERD ANIMALS + "tuna", + "salmon", + "mackerel", + "trout", + "bream", + "sea-bass", + "sea bass", + "carp", + "cod", + "flounder", + "whiting", + "moose", + # OTHER ODDITIES + "graffiti", + "djinn", + "samuri", + "offspring", + "pence", + "quid", + "hertz", +] + pl_sb_uninflected_s_complete +# SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE) + +pl_sb_uninflected_caps = [ + # ALL NATIONALS ENDING IN -ese + "Portuguese", + "Amoyese", + "Borghese", + "Congoese", + "Faroese", + "Foochowese", + "Genevese", + "Genoese", + "Gilbertese", + "Hottentotese", + "Kiplingese", + "Kongoese", + "Lucchese", + "Maltese", + "Nankingese", + "Niasese", + "Pekingese", + "Piedmontese", + "Pistoiese", + "Sarawakese", + "Shavese", + "Vermontese", + "Wenchowese", + "Yengeese", +] + + +pl_sb_uninflected_endings = [ + # UNCOUNTABLE NOUNS + "butter", + "cash", + "furniture", + "information", + # SOME FISH AND HERD ANIMALS + "fish", + "deer", + "sheep", + # ALL NATIONALS ENDING IN -ese + "nese", + "rese", + "lese", + "mese", + # DISEASES + "pox", + # OTHER ODDITIES + "craft", +] + pl_sb_uninflected_s_endings +# SOME WORDS ENDING IN ...s (OFTEN PAIRS TAKEN AS A WHOLE) + + +pl_sb_uninflected_bysize = bysize(pl_sb_uninflected_endings) + + +# SINGULAR WORDS ENDING IN ...s (ALL INFLECT WITH ...es) + +pl_sb_singular_s_complete = [ + "acropolis", + "aegis", + "alias", + "asbestos", + "bathos", + "bias", + "bronchitis", + "bursitis", + "caddis", + "cannabis", + "canvas", + "chaos", + "cosmos", + "dais", + "digitalis", + "epidermis", + "ethos", + "eyas", + "gas", + "glottis", + "hubris", + "ibis", + "lens", + "mantis", + "marquis", + "metropolis", + "pathos", + "pelvis", + "polis", + "rhinoceros", + "sassafras", + "trellis", +] + pl_sb_C_is_ides_complete + + +pl_sb_singular_s_endings = ["ss", "us"] + pl_sb_C_is_ides_endings + +pl_sb_singular_s_bysize = bysize(pl_sb_singular_s_endings) + +si_sb_singular_s_complete = [f"{w}es" for w in pl_sb_singular_s_complete] +si_sb_singular_s_endings = [f"{w}es" for w in pl_sb_singular_s_endings] +si_sb_singular_s_bysize = bysize(si_sb_singular_s_endings) + +pl_sb_singular_s_es = ["[A-Z].*es"] + +pl_sb_singular_s = enclose( + "|".join( + pl_sb_singular_s_complete + + [f".*{w}" for w in pl_sb_singular_s_endings] + + pl_sb_singular_s_es + ) +) + + +# PLURALS ENDING IN uses -> use + + +si_sb_ois_oi_case = ("Bolshois", "Hanois") + +si_sb_uses_use_case = ("Betelgeuses", "Duses", "Meuses", "Syracuses", "Toulouses") + +si_sb_uses_use = ( + "abuses", + "applauses", + "blouses", + "carouses", + "causes", + "chartreuses", + "clauses", + "contuses", + "douses", + "excuses", + "fuses", + "grouses", + "hypotenuses", + "masseuses", + "menopauses", + "misuses", + "muses", + "overuses", + "pauses", + "peruses", + "profuses", + "recluses", + "reuses", + "ruses", + "souses", + "spouses", + "suffuses", + "transfuses", + "uses", +) + +si_sb_ies_ie_case = ( + "Addies", + "Aggies", + "Allies", + "Amies", + "Angies", + "Annies", + "Annmaries", + "Archies", + "Arties", + "Aussies", + "Barbies", + "Barries", + "Basies", + "Bennies", + "Bernies", + "Berties", + "Bessies", + "Betties", + "Billies", + "Blondies", + "Bobbies", + "Bonnies", + "Bowies", + "Brandies", + "Bries", + "Brownies", + "Callies", + "Carnegies", + "Carries", + "Cassies", + "Charlies", + "Cheries", + "Christies", + "Connies", + "Curies", + "Dannies", + "Debbies", + "Dixies", + "Dollies", + "Donnies", + "Drambuies", + "Eddies", + "Effies", + "Ellies", + "Elsies", + "Eries", + "Ernies", + "Essies", + "Eugenies", + "Fannies", + "Flossies", + "Frankies", + "Freddies", + "Gillespies", + "Goldies", + "Gracies", + "Guthries", + "Hallies", + "Hatties", + "Hetties", + "Hollies", + "Jackies", + "Jamies", + "Janies", + "Jannies", + "Jeanies", + "Jeannies", + "Jennies", + "Jessies", + "Jimmies", + "Jodies", + "Johnies", + "Johnnies", + "Josies", + "Julies", + "Kalgoorlies", + "Kathies", + "Katies", + "Kellies", + "Kewpies", + "Kristies", + "Laramies", + "Lassies", + "Lauries", + "Leslies", + "Lessies", + "Lillies", + "Lizzies", + "Lonnies", + "Lories", + "Lorries", + "Lotties", + "Louies", + "Mackenzies", + "Maggies", + "Maisies", + "Mamies", + "Marcies", + "Margies", + "Maries", + "Marjories", + "Matties", + "McKenzies", + "Melanies", + "Mickies", + "Millies", + "Minnies", + "Mollies", + "Mounties", + "Nannies", + "Natalies", + "Nellies", + "Netties", + "Ollies", + "Ozzies", + "Pearlies", + "Pottawatomies", + "Reggies", + "Richies", + "Rickies", + "Robbies", + "Ronnies", + "Rosalies", + "Rosemaries", + "Rosies", + "Roxies", + "Rushdies", + "Ruthies", + "Sadies", + "Sallies", + "Sammies", + "Scotties", + "Selassies", + "Sherries", + "Sophies", + "Stacies", + "Stefanies", + "Stephanies", + "Stevies", + "Susies", + "Sylvies", + "Tammies", + "Terries", + "Tessies", + "Tommies", + "Tracies", + "Trekkies", + "Valaries", + "Valeries", + "Valkyries", + "Vickies", + "Virgies", + "Willies", + "Winnies", + "Wylies", + "Yorkies", +) + +si_sb_ies_ie = ( + "aeries", + "baggies", + "belies", + "biggies", + "birdies", + "bogies", + "bonnies", + "boogies", + "bookies", + "bourgeoisies", + "brownies", + "budgies", + "caddies", + "calories", + "camaraderies", + "cockamamies", + "collies", + "cookies", + "coolies", + "cooties", + "coteries", + "crappies", + "curies", + "cutesies", + "dogies", + "eyries", + "floozies", + "footsies", + "freebies", + "genies", + "goalies", + "groupies", + "hies", + "jalousies", + "junkies", + "kiddies", + "laddies", + "lassies", + "lies", + "lingeries", + "magpies", + "menageries", + "mommies", + "movies", + "neckties", + "newbies", + "nighties", + "oldies", + "organdies", + "overlies", + "pies", + "pinkies", + "pixies", + "potpies", + "prairies", + "quickies", + "reveries", + "rookies", + "rotisseries", + "softies", + "sorties", + "species", + "stymies", + "sweeties", + "ties", + "underlies", + "unties", + "veggies", + "vies", + "yuppies", + "zombies", +) + + +si_sb_oes_oe_case = ( + "Chloes", + "Crusoes", + "Defoes", + "Faeroes", + "Ivanhoes", + "Joes", + "McEnroes", + "Moes", + "Monroes", + "Noes", + "Poes", + "Roscoes", + "Tahoes", + "Tippecanoes", + "Zoes", +) + +si_sb_oes_oe = ( + "aloes", + "backhoes", + "canoes", + "does", + "floes", + "foes", + "hoes", + "mistletoes", + "oboes", + "pekoes", + "roes", + "sloes", + "throes", + "tiptoes", + "toes", + "woes", +) + +si_sb_z_zes = ("quartzes", "topazes") + +si_sb_zzes_zz = ("buzzes", "fizzes", "frizzes", "razzes") + +si_sb_ches_che_case = ( + "Andromaches", + "Apaches", + "Blanches", + "Comanches", + "Nietzsches", + "Porsches", + "Roches", +) + +si_sb_ches_che = ( + "aches", + "avalanches", + "backaches", + "bellyaches", + "caches", + "cloches", + "creches", + "douches", + "earaches", + "fiches", + "headaches", + "heartaches", + "microfiches", + "niches", + "pastiches", + "psyches", + "quiches", + "stomachaches", + "toothaches", + "tranches", +) + +si_sb_xes_xe = ("annexes", "axes", "deluxes", "pickaxes") + +si_sb_sses_sse_case = ("Hesses", "Jesses", "Larousses", "Matisses") +si_sb_sses_sse = ( + "bouillabaisses", + "crevasses", + "demitasses", + "impasses", + "mousses", + "posses", +) + +si_sb_ves_ve_case = ( + # *[nwl]ives -> [nwl]live + "Clives", + "Palmolives", +) +si_sb_ves_ve = ( + # *[^d]eaves -> eave + "interweaves", + "weaves", + # *[nwl]ives -> [nwl]live + "olives", + # *[eoa]lves -> [eoa]lve + "bivalves", + "dissolves", + "resolves", + "salves", + "twelves", + "valves", +) + + +plverb_special_s = enclose( + "|".join( + [pl_sb_singular_s] + + pl_sb_uninflected_s + + list(pl_sb_irregular_s) + + ["(.*[csx])is", "(.*)ceps", "[A-Z].*s"] + ) +) + +_pl_sb_postfix_adj_defn = ( + ("general", enclose(r"(?!major|lieutenant|brigadier|adjutant|.*star)\S+")), + ("martial", enclose("court")), + ("force", enclose("pound")), +) + +pl_sb_postfix_adj: Iterable[str] = ( + enclose(val + f"(?=(?:-|\\s+){key})") for key, val in _pl_sb_postfix_adj_defn +) + +pl_sb_postfix_adj_stems = f"({'|'.join(pl_sb_postfix_adj)})(.*)" + + +# PLURAL WORDS ENDING IS es GO TO SINGULAR is + +si_sb_es_is = ( + "amanuenses", + "amniocenteses", + "analyses", + "antitheses", + "apotheoses", + "arterioscleroses", + "atheroscleroses", + "axes", + # 'bases', # bases -> basis + "catalyses", + "catharses", + "chasses", + "cirrhoses", + "cocces", + "crises", + "diagnoses", + "dialyses", + "diereses", + "electrolyses", + "emphases", + "exegeses", + "geneses", + "halitoses", + "hydrolyses", + "hypnoses", + "hypotheses", + "hystereses", + "metamorphoses", + "metastases", + "misdiagnoses", + "mitoses", + "mononucleoses", + "narcoses", + "necroses", + "nemeses", + "neuroses", + "oases", + "osmoses", + "osteoporoses", + "paralyses", + "parentheses", + "parthenogeneses", + "periphrases", + "photosyntheses", + "probosces", + "prognoses", + "prophylaxes", + "prostheses", + "preces", + "psoriases", + "psychoanalyses", + "psychokineses", + "psychoses", + "scleroses", + "scolioses", + "sepses", + "silicoses", + "symbioses", + "synopses", + "syntheses", + "taxes", + "telekineses", + "theses", + "thromboses", + "tuberculoses", + "urinalyses", +) + +pl_prep_list = """ + about above across after among around at athwart before behind + below beneath beside besides between betwixt beyond but by + during except for from in into near of off on onto out over + since till to under until unto upon with""".split() + +pl_prep_list_da = pl_prep_list + ["de", "du", "da"] + +pl_prep_bysize = bysize(pl_prep_list_da) + +pl_prep = enclose("|".join(pl_prep_list_da)) + +pl_sb_prep_dual_compound = fr"(.*?)((?:-|\s+)(?:{pl_prep})(?:-|\s+))a(?:-|\s+)(.*)" + + +singular_pronoun_genders = { + "neuter", + "feminine", + "masculine", + "gender-neutral", + "feminine or masculine", + "masculine or feminine", +} + +pl_pron_nom = { + # NOMINATIVE REFLEXIVE + "i": "we", + "myself": "ourselves", + "you": "you", + "yourself": "yourselves", + "she": "they", + "herself": "themselves", + "he": "they", + "himself": "themselves", + "it": "they", + "itself": "themselves", + "they": "they", + "themself": "themselves", + # POSSESSIVE + "mine": "ours", + "yours": "yours", + "hers": "theirs", + "his": "theirs", + "its": "theirs", + "theirs": "theirs", +} + +si_pron: Dict[str, Dict[str, Union[str, Dict[str, str]]]] = { + "nom": {v: k for (k, v) in pl_pron_nom.items()} +} +si_pron["nom"]["we"] = "I" + + +pl_pron_acc = { + # ACCUSATIVE REFLEXIVE + "me": "us", + "myself": "ourselves", + "you": "you", + "yourself": "yourselves", + "her": "them", + "herself": "themselves", + "him": "them", + "himself": "themselves", + "it": "them", + "itself": "themselves", + "them": "them", + "themself": "themselves", +} + +pl_pron_acc_keys = enclose("|".join(pl_pron_acc)) +pl_pron_acc_keys_bysize = bysize(pl_pron_acc) + +si_pron["acc"] = {v: k for (k, v) in pl_pron_acc.items()} + +for _thecase, _plur, _gend, _sing in ( + ("nom", "they", "neuter", "it"), + ("nom", "they", "feminine", "she"), + ("nom", "they", "masculine", "he"), + ("nom", "they", "gender-neutral", "they"), + ("nom", "they", "feminine or masculine", "she or he"), + ("nom", "they", "masculine or feminine", "he or she"), + ("nom", "themselves", "neuter", "itself"), + ("nom", "themselves", "feminine", "herself"), + ("nom", "themselves", "masculine", "himself"), + ("nom", "themselves", "gender-neutral", "themself"), + ("nom", "themselves", "feminine or masculine", "herself or himself"), + ("nom", "themselves", "masculine or feminine", "himself or herself"), + ("nom", "theirs", "neuter", "its"), + ("nom", "theirs", "feminine", "hers"), + ("nom", "theirs", "masculine", "his"), + ("nom", "theirs", "gender-neutral", "theirs"), + ("nom", "theirs", "feminine or masculine", "hers or his"), + ("nom", "theirs", "masculine or feminine", "his or hers"), + ("acc", "them", "neuter", "it"), + ("acc", "them", "feminine", "her"), + ("acc", "them", "masculine", "him"), + ("acc", "them", "gender-neutral", "them"), + ("acc", "them", "feminine or masculine", "her or him"), + ("acc", "them", "masculine or feminine", "him or her"), + ("acc", "themselves", "neuter", "itself"), + ("acc", "themselves", "feminine", "herself"), + ("acc", "themselves", "masculine", "himself"), + ("acc", "themselves", "gender-neutral", "themself"), + ("acc", "themselves", "feminine or masculine", "herself or himself"), + ("acc", "themselves", "masculine or feminine", "himself or herself"), +): + try: + si_pron[_thecase][_plur][_gend] = _sing # type: ignore + except TypeError: + si_pron[_thecase][_plur] = {} + si_pron[_thecase][_plur][_gend] = _sing # type: ignore + + +si_pron_acc_keys = enclose("|".join(si_pron["acc"])) +si_pron_acc_keys_bysize = bysize(si_pron["acc"]) + + +def get_si_pron(thecase, word, gender) -> str: + try: + sing = si_pron[thecase][word] + except KeyError: + raise # not a pronoun + try: + return sing[gender] # has several types due to gender + except TypeError: + return cast(str, sing) # answer independent of gender + + +# These dictionaries group verbs by first, second and third person +# conjugations. + +plverb_irregular_pres = { + "am": "are", + "are": "are", + "is": "are", + "was": "were", + "were": "were", + "was": "were", + "have": "have", + "have": "have", + "has": "have", + "do": "do", + "do": "do", + "does": "do", +} + +plverb_ambiguous_pres = { + "act": "act", + "act": "act", + "acts": "act", + "blame": "blame", + "blame": "blame", + "blames": "blame", + "can": "can", + "can": "can", + "can": "can", + "must": "must", + "must": "must", + "must": "must", + "fly": "fly", + "fly": "fly", + "flies": "fly", + "copy": "copy", + "copy": "copy", + "copies": "copy", + "drink": "drink", + "drink": "drink", + "drinks": "drink", + "fight": "fight", + "fight": "fight", + "fights": "fight", + "fire": "fire", + "fire": "fire", + "fires": "fire", + "like": "like", + "like": "like", + "likes": "like", + "look": "look", + "look": "look", + "looks": "look", + "make": "make", + "make": "make", + "makes": "make", + "reach": "reach", + "reach": "reach", + "reaches": "reach", + "run": "run", + "run": "run", + "runs": "run", + "sink": "sink", + "sink": "sink", + "sinks": "sink", + "sleep": "sleep", + "sleep": "sleep", + "sleeps": "sleep", + "view": "view", + "view": "view", + "views": "view", +} + +plverb_ambiguous_pres_keys = re.compile( + fr"^({enclose('|'.join(plverb_ambiguous_pres))})((\s.*)?)$", re.IGNORECASE +) + + +plverb_irregular_non_pres = ( + "did", + "had", + "ate", + "made", + "put", + "spent", + "fought", + "sank", + "gave", + "sought", + "shall", + "could", + "ought", + "should", +) + +plverb_ambiguous_non_pres = re.compile( + r"^((?:thought|saw|bent|will|might|cut))((\s.*)?)$", re.IGNORECASE +) + +# "..oes" -> "..oe" (the rest are "..oes" -> "o") + +pl_v_oes_oe = ("canoes", "floes", "oboes", "roes", "throes", "woes") +pl_v_oes_oe_endings_size4 = ("hoes", "toes") +pl_v_oes_oe_endings_size5 = ("shoes",) + + +pl_count_zero = ("0", "no", "zero", "nil") + + +pl_count_one = ("1", "a", "an", "one", "each", "every", "this", "that") + +pl_adj_special = {"a": "some", "an": "some", "this": "these", "that": "those"} + +pl_adj_special_keys = re.compile( + fr"^({enclose('|'.join(pl_adj_special))})$", re.IGNORECASE +) + +pl_adj_poss = { + "my": "our", + "your": "your", + "its": "their", + "her": "their", + "his": "their", + "their": "their", +} + +pl_adj_poss_keys = re.compile(fr"^({enclose('|'.join(pl_adj_poss))})$", re.IGNORECASE) + + +# 2. INDEFINITE ARTICLES + +# THIS PATTERN MATCHES STRINGS OF CAPITALS STARTING WITH A "VOWEL-SOUND" +# CONSONANT FOLLOWED BY ANOTHER CONSONANT, AND WHICH ARE NOT LIKELY +# TO BE REAL WORDS (OH, ALL RIGHT THEN, IT'S JUST MAGIC!) + +A_abbrev = re.compile( + r""" +(?! FJO | [HLMNS]Y. | RY[EO] | SQU + | ( F[LR]? | [HL] | MN? | N | RH? | S[CHKLMNPTVW]? | X(YL)?) [AEIOU]) +[FHLMNRSX][A-Z] +""", + re.VERBOSE, +) + +# THIS PATTERN CODES THE BEGINNINGS OF ALL ENGLISH WORDS BEGINING WITH A +# 'y' FOLLOWED BY A CONSONANT. ANY OTHER Y-CONSONANT PREFIX THEREFORE +# IMPLIES AN ABBREVIATION. + +A_y_cons = re.compile(r"^(y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt))", re.IGNORECASE) + +# EXCEPTIONS TO EXCEPTIONS + +A_explicit_a = re.compile(r"^((?:unabomber|unanimous|US))", re.IGNORECASE) + +A_explicit_an = re.compile( + r"^((?:euler|hour(?!i)|heir|honest|hono[ur]|mpeg))", re.IGNORECASE +) + +A_ordinal_an = re.compile(r"^([aefhilmnorsx]-?th)", re.IGNORECASE) + +A_ordinal_a = re.compile(r"^([bcdgjkpqtuvwyz]-?th)", re.IGNORECASE) + + +# NUMERICAL INFLECTIONS + +nth = { + 0: "th", + 1: "st", + 2: "nd", + 3: "rd", + 4: "th", + 5: "th", + 6: "th", + 7: "th", + 8: "th", + 9: "th", + 11: "th", + 12: "th", + 13: "th", +} +nth_suff = set(nth.values()) + +ordinal = dict( + ty="tieth", + one="first", + two="second", + three="third", + five="fifth", + eight="eighth", + nine="ninth", + twelve="twelfth", +) + +ordinal_suff = re.compile(fr"({'|'.join(ordinal)})\Z") + + +# NUMBERS + +unit = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] +teen = [ + "ten", + "eleven", + "twelve", + "thirteen", + "fourteen", + "fifteen", + "sixteen", + "seventeen", + "eighteen", + "nineteen", +] +ten = [ + "", + "", + "twenty", + "thirty", + "forty", + "fifty", + "sixty", + "seventy", + "eighty", + "ninety", +] +mill = [ + " ", + " thousand", + " million", + " billion", + " trillion", + " quadrillion", + " quintillion", + " sextillion", + " septillion", + " octillion", + " nonillion", + " decillion", +] + + +# SUPPORT CLASSICAL PLURALIZATIONS + +def_classical = dict( + all=False, zero=False, herd=False, names=True, persons=False, ancient=False +) + +all_classical = {k: True for k in def_classical} +no_classical = {k: False for k in def_classical} + + +# Maps strings to built-in constant types +string_to_constant = {"True": True, "False": False, "None": None} + + +# Pre-compiled regular expression objects +DOLLAR_DIGITS = re.compile(r"\$(\d+)") +FUNCTION_CALL = re.compile(r"((\w+)\([^)]*\)*)", re.IGNORECASE) +PARTITION_WORD = re.compile(r"\A(\s*)(.+?)(\s*)\Z") +PL_SB_POSTFIX_ADJ_STEMS_RE = re.compile( + fr"^(?:{pl_sb_postfix_adj_stems})$", re.IGNORECASE +) +PL_SB_PREP_DUAL_COMPOUND_RE = re.compile( + fr"^(?:{pl_sb_prep_dual_compound})$", re.IGNORECASE +) +DENOMINATOR = re.compile(r"(?P.+)( (per|a) .+)") +PLVERB_SPECIAL_S_RE = re.compile(fr"^({plverb_special_s})$") +WHITESPACE = re.compile(r"\s") +ENDS_WITH_S = re.compile(r"^(.*[^s])s$", re.IGNORECASE) +ENDS_WITH_APOSTROPHE_S = re.compile(r"^(.*)'s?$") +INDEFINITE_ARTICLE_TEST = re.compile(r"\A(\s*)(?:an?\s+)?(.+?)(\s*)\Z", re.IGNORECASE) +SPECIAL_AN = re.compile(r"^[aefhilmnorsx]$", re.IGNORECASE) +SPECIAL_A = re.compile(r"^[bcdgjkpqtuvwyz]$", re.IGNORECASE) +SPECIAL_ABBREV_AN = re.compile(r"^[aefhilmnorsx][.-]", re.IGNORECASE) +SPECIAL_ABBREV_A = re.compile(r"^[a-z][.-]", re.IGNORECASE) +CONSONANTS = re.compile(r"^[^aeiouy]", re.IGNORECASE) +ARTICLE_SPECIAL_EU = re.compile(r"^e[uw]", re.IGNORECASE) +ARTICLE_SPECIAL_ONCE = re.compile(r"^onc?e\b", re.IGNORECASE) +ARTICLE_SPECIAL_ONETIME = re.compile(r"^onetime\b", re.IGNORECASE) +ARTICLE_SPECIAL_UNIT = re.compile(r"^uni([^nmd]|mo)", re.IGNORECASE) +ARTICLE_SPECIAL_UBA = re.compile(r"^u[bcfghjkqrst][aeiou]", re.IGNORECASE) +ARTICLE_SPECIAL_UKR = re.compile(r"^ukr", re.IGNORECASE) +SPECIAL_CAPITALS = re.compile(r"^U[NK][AIEO]?") +VOWELS = re.compile(r"^[aeiou]", re.IGNORECASE) + +DIGIT_GROUP = re.compile(r"(\d)") +TWO_DIGITS = re.compile(r"(\d)(\d)") +THREE_DIGITS = re.compile(r"(\d)(\d)(\d)") +THREE_DIGITS_WORD = re.compile(r"(\d)(\d)(\d)(?=\D*\Z)") +TWO_DIGITS_WORD = re.compile(r"(\d)(\d)(?=\D*\Z)") +ONE_DIGIT_WORD = re.compile(r"(\d)(?=\D*\Z)") + +FOUR_DIGIT_COMMA = re.compile(r"(\d)(\d{3}(?:,|\Z))") +NON_DIGIT = re.compile(r"\D") +WHITESPACES_COMMA = re.compile(r"\s+,") +COMMA_WORD = re.compile(r", (\S+)\s+\Z") +WHITESPACES = re.compile(r"\s+") + + +PRESENT_PARTICIPLE_REPLACEMENTS = ( + (re.compile(r"ie$"), r"y"), + ( + re.compile(r"ue$"), + r"u", + ), # TODO: isn't ue$ -> u encompassed in the following rule? + (re.compile(r"([auy])e$"), r"\g<1>"), + (re.compile(r"ski$"), r"ski"), + (re.compile(r"[^b]i$"), r""), + (re.compile(r"^(are|were)$"), r"be"), + (re.compile(r"^(had)$"), r"hav"), + (re.compile(r"^(hoe)$"), r"\g<1>"), + (re.compile(r"([^e])e$"), r"\g<1>"), + (re.compile(r"er$"), r"er"), + (re.compile(r"([^aeiou][aeiouy]([bdgmnprst]))$"), r"\g<1>\g<2>"), +) + +DIGIT = re.compile(r"\d") + + +class Words(str): + lowered: str + split_: List[str] + first: str + last: str + + def __init__(self, orig) -> None: + self.lowered = self.lower() + self.split_ = self.split() + self.first = self.split_[0] + self.last = self.split_[-1] + + +Word = Annotated[str, Field(min_length=1)] +Falsish = Any # ideally, falsish would only validate on bool(value) is False + + +class engine: + def __init__(self) -> None: + + self.classical_dict = def_classical.copy() + self.persistent_count: Optional[int] = None + self.mill_count = 0 + self.pl_sb_user_defined: List[str] = [] + self.pl_v_user_defined: List[str] = [] + self.pl_adj_user_defined: List[str] = [] + self.si_sb_user_defined: List[str] = [] + self.A_a_user_defined: List[str] = [] + self.thegender = "neuter" + self.__number_args: Optional[Dict[str, str]] = None + + @property + def _number_args(self): + return cast(Dict[str, str], self.__number_args) + + @_number_args.setter + def _number_args(self, val): + self.__number_args = val + + deprecated_methods = dict( + pl="plural", + plnoun="plural_noun", + plverb="plural_verb", + pladj="plural_adj", + sinoun="single_noun", + prespart="present_participle", + numwords="number_to_words", + plequal="compare", + plnounequal="compare_nouns", + plverbequal="compare_verbs", + pladjequal="compare_adjs", + wordlist="join", + ) + + def __getattr__(self, meth): + if meth in self.deprecated_methods: + print3(f"{meth}() deprecated, use {self.deprecated_methods[meth]}()") + raise DeprecationWarning + raise AttributeError + + def defnoun(self, singular: str, plural: str) -> int: + """ + Set the noun plural of singular to plural. + + """ + self.checkpat(singular) + self.checkpatplural(plural) + self.pl_sb_user_defined.extend((singular, plural)) + self.si_sb_user_defined.extend((plural, singular)) + return 1 + + def defverb(self, s1: str, p1: str, s2: str, p2: str, s3: str, p3: str) -> int: + """ + Set the verb plurals for s1, s2 and s3 to p1, p2 and p3 respectively. + + Where 1, 2 and 3 represent the 1st, 2nd and 3rd person forms of the verb. + + """ + self.checkpat(s1) + self.checkpat(s2) + self.checkpat(s3) + self.checkpatplural(p1) + self.checkpatplural(p2) + self.checkpatplural(p3) + self.pl_v_user_defined.extend((s1, p1, s2, p2, s3, p3)) + return 1 + + def defadj(self, singular: str, plural: str) -> int: + """ + Set the adjective plural of singular to plural. + + """ + self.checkpat(singular) + self.checkpatplural(plural) + self.pl_adj_user_defined.extend((singular, plural)) + return 1 + + def defa(self, pattern: str) -> int: + """ + Define the indefinite article as 'a' for words matching pattern. + + """ + self.checkpat(pattern) + self.A_a_user_defined.extend((pattern, "a")) + return 1 + + def defan(self, pattern: str) -> int: + """ + Define the indefinite article as 'an' for words matching pattern. + + """ + self.checkpat(pattern) + self.A_a_user_defined.extend((pattern, "an")) + return 1 + + def checkpat(self, pattern: Optional[str]) -> None: + """ + check for errors in a regex pattern + """ + if pattern is None: + return + try: + re.match(pattern, "") + except re.error: + print3(f"\nBad user-defined singular pattern:\n\t{pattern}\n") + raise BadUserDefinedPatternError + + def checkpatplural(self, pattern: str) -> None: + """ + check for errors in a regex replace pattern + """ + return + + @validate_arguments + def ud_match(self, word: Word, wordlist: Sequence[Optional[Word]]) -> Optional[str]: + for i in range(len(wordlist) - 2, -2, -2): # backwards through even elements + mo = re.search(fr"^{wordlist[i]}$", word, re.IGNORECASE) + if mo: + if wordlist[i + 1] is None: + return None + pl = DOLLAR_DIGITS.sub( + r"\\1", cast(Word, wordlist[i + 1]) + ) # change $n to \n for expand + return mo.expand(pl) + return None + + def classical(self, **kwargs) -> None: + """ + turn classical mode on and off for various categories + + turn on all classical modes: + classical() + classical(all=True) + + turn on or off specific claassical modes: + e.g. + classical(herd=True) + classical(names=False) + + By default all classical modes are off except names. + + unknown value in args or key in kwargs raises + exception: UnknownClasicalModeError + + """ + if not kwargs: + self.classical_dict = all_classical.copy() + return + if "all" in kwargs: + if kwargs["all"]: + self.classical_dict = all_classical.copy() + else: + self.classical_dict = no_classical.copy() + + for k, v in kwargs.items(): + if k in def_classical: + self.classical_dict[k] = v + else: + raise UnknownClassicalModeError + + def num( + self, count: Optional[int] = None, show: Optional[int] = None + ) -> str: # (;$count,$show) + """ + Set the number to be used in other method calls. + + Returns count. + + Set show to False to return '' instead. + + """ + if count is not None: + try: + self.persistent_count = int(count) + except ValueError: + raise BadNumValueError + if (show is None) or show: + return str(count) + else: + self.persistent_count = None + return "" + + def gender(self, gender: str) -> None: + """ + set the gender for the singular of plural pronouns + + can be one of: + 'neuter' ('they' -> 'it') + 'feminine' ('they' -> 'she') + 'masculine' ('they' -> 'he') + 'gender-neutral' ('they' -> 'they') + 'feminine or masculine' ('they' -> 'she or he') + 'masculine or feminine' ('they' -> 'he or she') + """ + if gender in singular_pronoun_genders: + self.thegender = gender + else: + raise BadGenderError + + def _get_value_from_ast(self, obj): + """ + Return the value of the ast object. + """ + if isinstance(obj, ast.Num): + return obj.n + elif isinstance(obj, ast.Str): + return obj.s + elif isinstance(obj, ast.List): + return [self._get_value_from_ast(e) for e in obj.elts] + elif isinstance(obj, ast.Tuple): + return tuple([self._get_value_from_ast(e) for e in obj.elts]) + + # None, True and False are NameConstants in Py3.4 and above. + elif isinstance(obj, ast.NameConstant): + return obj.value + + # Probably passed a variable name. + # Or passed a single word without wrapping it in quotes as an argument + # ex: p.inflect("I plural(see)") instead of p.inflect("I plural('see')") + raise NameError(f"name '{obj.id}' is not defined") + + def _string_to_substitute( + self, mo: Match, methods_dict: Dict[str, Callable] + ) -> str: + """ + Return the string to be substituted for the match. + """ + matched_text, f_name = mo.groups() + # matched_text is the complete match string. e.g. plural_noun(cat) + # f_name is the function name. e.g. plural_noun + + # Return matched_text if function name is not in methods_dict + if f_name not in methods_dict: + return matched_text + + # Parse the matched text + a_tree = ast.parse(matched_text) + + # get the args and kwargs from ast objects + args_list = [ + self._get_value_from_ast(a) + for a in a_tree.body[0].value.args # type: ignore[attr-defined] + ] + kwargs_list = { + kw.arg: self._get_value_from_ast(kw.value) + for kw in a_tree.body[0].value.keywords # type: ignore[attr-defined] + } + + # Call the corresponding function + return methods_dict[f_name](*args_list, **kwargs_list) + + # 0. PERFORM GENERAL INFLECTIONS IN A STRING + + @validate_arguments + def inflect(self, text: Word) -> str: + """ + Perform inflections in a string. + + e.g. inflect('The plural of cat is plural(cat)') returns + 'The plural of cat is cats' + + can use plural, plural_noun, plural_verb, plural_adj, + singular_noun, a, an, no, ordinal, number_to_words, + and prespart + + """ + save_persistent_count = self.persistent_count + + # Dictionary of allowed methods + methods_dict: Dict[str, Callable] = { + "plural": self.plural, + "plural_adj": self.plural_adj, + "plural_noun": self.plural_noun, + "plural_verb": self.plural_verb, + "singular_noun": self.singular_noun, + "a": self.a, + "an": self.a, + "no": self.no, + "ordinal": self.ordinal, + "number_to_words": self.number_to_words, + "present_participle": self.present_participle, + "num": self.num, + } + + # Regular expression to find Python's function call syntax + output = FUNCTION_CALL.sub( + lambda mo: self._string_to_substitute(mo, methods_dict), text + ) + self.persistent_count = save_persistent_count + return output + + # ## PLURAL SUBROUTINES + + def postprocess(self, orig: str, inflected) -> str: + inflected = str(inflected) + if "|" in inflected: + word_options = inflected.split("|") + # When two parts of a noun need to be pluralized + if len(word_options[0].split(" ")) == len(word_options[1].split(" ")): + result = inflected.split("|")[self.classical_dict["all"]].split(" ") + # When only the last part of the noun needs to be pluralized + else: + result = inflected.split(" ") + for index, word in enumerate(result): + if "|" in word: + result[index] = word.split("|")[self.classical_dict["all"]] + else: + result = inflected.split(" ") + + # Try to fix word wise capitalization + for index, word in enumerate(orig.split(" ")): + if word == "I": + # Is this the only word for exceptions like this + # Where the original is fully capitalized + # without 'meaning' capitalization? + # Also this fails to handle a capitalizaion in context + continue + if word.capitalize() == word: + result[index] = result[index].capitalize() + if word == word.upper(): + result[index] = result[index].upper() + return " ".join(result) + + def partition_word(self, text: str) -> Tuple[str, str, str]: + mo = PARTITION_WORD.search(text) + if mo: + return mo.group(1), mo.group(2), mo.group(3) + else: + return "", "", "" + + @validate_arguments + def plural(self, text: Word, count: Optional[Union[str, int, Any]] = None) -> str: + """ + Return the plural of text. + + If count supplied, then return text if count is one of: + 1, a, an, one, each, every, this, that + + otherwise return the plural. + + Whitespace at the start and end is preserved. + + """ + pre, word, post = self.partition_word(text) + if not word: + return text + plural = self.postprocess( + word, + self._pl_special_adjective(word, count) + or self._pl_special_verb(word, count) + or self._plnoun(word, count), + ) + return f"{pre}{plural}{post}" + + @validate_arguments + def plural_noun( + self, text: Word, count: Optional[Union[str, int, Any]] = None + ) -> str: + """ + Return the plural of text, where text is a noun. + + If count supplied, then return text if count is one of: + 1, a, an, one, each, every, this, that + + otherwise return the plural. + + Whitespace at the start and end is preserved. + + """ + pre, word, post = self.partition_word(text) + if not word: + return text + plural = self.postprocess(word, self._plnoun(word, count)) + return f"{pre}{plural}{post}" + + @validate_arguments + def plural_verb( + self, text: Word, count: Optional[Union[str, int, Any]] = None + ) -> str: + """ + Return the plural of text, where text is a verb. + + If count supplied, then return text if count is one of: + 1, a, an, one, each, every, this, that + + otherwise return the plural. + + Whitespace at the start and end is preserved. + + """ + pre, word, post = self.partition_word(text) + if not word: + return text + plural = self.postprocess( + word, + self._pl_special_verb(word, count) or self._pl_general_verb(word, count), + ) + return f"{pre}{plural}{post}" + + @validate_arguments + def plural_adj( + self, text: Word, count: Optional[Union[str, int, Any]] = None + ) -> str: + """ + Return the plural of text, where text is an adjective. + + If count supplied, then return text if count is one of: + 1, a, an, one, each, every, this, that + + otherwise return the plural. + + Whitespace at the start and end is preserved. + + """ + pre, word, post = self.partition_word(text) + if not word: + return text + plural = self.postprocess(word, self._pl_special_adjective(word, count) or word) + return f"{pre}{plural}{post}" + + @validate_arguments + def compare(self, word1: Word, word2: Word) -> Union[str, bool]: + """ + compare word1 and word2 for equality regardless of plurality + + return values: + eq - the strings are equal + p:s - word1 is the plural of word2 + s:p - word2 is the plural of word1 + p:p - word1 and word2 are two different plural forms of the one word + False - otherwise + + >>> compare = engine().compare + >>> compare("egg", "eggs") + 's:p' + >>> compare('egg', 'egg') + 'eq' + + Words should not be empty. + + >>> compare('egg', '') + Traceback (most recent call last): + ... + pydantic.error_wrappers.ValidationError: 1 validation error for Compare + word2 + ensure this value has at least 1 characters... + """ + norms = self.plural_noun, self.plural_verb, self.plural_adj + results = (self._plequal(word1, word2, norm) for norm in norms) + return next(filter(None, results), False) + + @validate_arguments + def compare_nouns(self, word1: Word, word2: Word) -> Union[str, bool]: + """ + compare word1 and word2 for equality regardless of plurality + word1 and word2 are to be treated as nouns + + return values: + eq - the strings are equal + p:s - word1 is the plural of word2 + s:p - word2 is the plural of word1 + p:p - word1 and word2 are two different plural forms of the one word + False - otherwise + + """ + return self._plequal(word1, word2, self.plural_noun) + + @validate_arguments + def compare_verbs(self, word1: Word, word2: Word) -> Union[str, bool]: + """ + compare word1 and word2 for equality regardless of plurality + word1 and word2 are to be treated as verbs + + return values: + eq - the strings are equal + p:s - word1 is the plural of word2 + s:p - word2 is the plural of word1 + p:p - word1 and word2 are two different plural forms of the one word + False - otherwise + + """ + return self._plequal(word1, word2, self.plural_verb) + + @validate_arguments + def compare_adjs(self, word1: Word, word2: Word) -> Union[str, bool]: + """ + compare word1 and word2 for equality regardless of plurality + word1 and word2 are to be treated as adjectives + + return values: + eq - the strings are equal + p:s - word1 is the plural of word2 + s:p - word2 is the plural of word1 + p:p - word1 and word2 are two different plural forms of the one word + False - otherwise + + """ + return self._plequal(word1, word2, self.plural_adj) + + @validate_arguments + def singular_noun( + self, + text: Word, + count: Optional[Union[int, str, Any]] = None, + gender: Optional[str] = None, + ) -> Union[str, bool]: + """ + Return the singular of text, where text is a plural noun. + + If count supplied, then return the singular if count is one of: + 1, a, an, one, each, every, this, that or if count is None + + otherwise return text unchanged. + + Whitespace at the start and end is preserved. + + >>> p = engine() + >>> p.singular_noun('horses') + 'horse' + >>> p.singular_noun('knights') + 'knight' + + Returns False when a singular noun is passed. + + >>> p.singular_noun('horse') + False + >>> p.singular_noun('knight') + False + >>> p.singular_noun('soldier') + False + + """ + pre, word, post = self.partition_word(text) + if not word: + return text + sing = self._sinoun(word, count=count, gender=gender) + if sing is not False: + plural = self.postprocess(word, sing) + return f"{pre}{plural}{post}" + return False + + def _plequal(self, word1: str, word2: str, pl) -> Union[str, bool]: # noqa: C901 + classval = self.classical_dict.copy() + self.classical_dict = all_classical.copy() + if word1 == word2: + return "eq" + if word1 == pl(word2): + return "p:s" + if pl(word1) == word2: + return "s:p" + self.classical_dict = no_classical.copy() + if word1 == pl(word2): + return "p:s" + if pl(word1) == word2: + return "s:p" + self.classical_dict = classval.copy() + + if pl == self.plural or pl == self.plural_noun: + if self._pl_check_plurals_N(word1, word2): + return "p:p" + if self._pl_check_plurals_N(word2, word1): + return "p:p" + if pl == self.plural or pl == self.plural_adj: + if self._pl_check_plurals_adj(word1, word2): + return "p:p" + return False + + def _pl_reg_plurals(self, pair: str, stems: str, end1: str, end2: str) -> bool: + pattern = fr"({stems})({end1}\|\1{end2}|{end2}\|\1{end1})" + return bool(re.search(pattern, pair)) + + def _pl_check_plurals_N(self, word1: str, word2: str) -> bool: + stem_endings = ( + (pl_sb_C_a_ata, "as", "ata"), + (pl_sb_C_is_ides, "is", "ides"), + (pl_sb_C_a_ae, "s", "e"), + (pl_sb_C_en_ina, "ens", "ina"), + (pl_sb_C_um_a, "ums", "a"), + (pl_sb_C_us_i, "uses", "i"), + (pl_sb_C_on_a, "ons", "a"), + (pl_sb_C_o_i_stems, "os", "i"), + (pl_sb_C_ex_ices, "exes", "ices"), + (pl_sb_C_ix_ices, "ixes", "ices"), + (pl_sb_C_i, "s", "i"), + (pl_sb_C_im, "s", "im"), + (".*eau", "s", "x"), + (".*ieu", "s", "x"), + (".*tri", "xes", "ces"), + (".{2,}[yia]n", "xes", "ges"), + ) + + words = map(Words, (word1, word2)) + pair = "|".join(word.last for word in words) + + return ( + pair in pl_sb_irregular_s.values() + or pair in pl_sb_irregular.values() + or pair in pl_sb_irregular_caps.values() + or any( + self._pl_reg_plurals(pair, stems, end1, end2) + for stems, end1, end2 in stem_endings + ) + ) + + def _pl_check_plurals_adj(self, word1: str, word2: str) -> bool: + word1a = word1[: word1.rfind("'")] if word1.endswith(("'s", "'")) else "" + word2a = word2[: word2.rfind("'")] if word2.endswith(("'s", "'")) else "" + + return ( + bool(word1a) + and bool(word2a) + and ( + self._pl_check_plurals_N(word1a, word2a) + or self._pl_check_plurals_N(word2a, word1a) + ) + ) + + def get_count(self, count: Optional[Union[str, int]] = None) -> Union[str, int]: + if count is None and self.persistent_count is not None: + count = self.persistent_count + + if count is not None: + count = ( + 1 + if ( + (str(count) in pl_count_one) + or ( + self.classical_dict["zero"] + and str(count).lower() in pl_count_zero + ) + ) + else 2 + ) + else: + count = "" + return count + + # @profile + def _plnoun( # noqa: C901 + self, word: str, count: Optional[Union[str, int]] = None + ) -> str: + count = self.get_count(count) + + # DEFAULT TO PLURAL + + if count == 1: + return word + + # HANDLE USER-DEFINED NOUNS + + value = self.ud_match(word, self.pl_sb_user_defined) + if value is not None: + return value + + # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS + + if word == "": + return word + + word = Words(word) + + if word.last.lower() in pl_sb_uninflected_complete: + return word + + if word in pl_sb_uninflected_caps: + return word + + for k, v in pl_sb_uninflected_bysize.items(): + if word.lowered[-k:] in v: + return word + + if self.classical_dict["herd"] and word.last.lower() in pl_sb_uninflected_herd: + return word + + # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.) + + mo = PL_SB_POSTFIX_ADJ_STEMS_RE.search(word) + if mo and mo.group(2) != "": + return f"{self._plnoun(mo.group(1), 2)}{mo.group(2)}" + + if " a " in word.lowered or "-a-" in word.lowered: + mo = PL_SB_PREP_DUAL_COMPOUND_RE.search(word) + if mo and mo.group(2) != "" and mo.group(3) != "": + return ( + f"{self._plnoun(mo.group(1), 2)}" + f"{mo.group(2)}" + f"{self._plnoun(mo.group(3))}" + ) + + if len(word.split_) >= 3: + for numword in range(1, len(word.split_) - 1): + if word.split_[numword] in pl_prep_list_da: + return " ".join( + word.split_[: numword - 1] + + [self._plnoun(word.split_[numword - 1], 2)] + + word.split_[numword:] + ) + + # only pluralize denominators in units + mo = DENOMINATOR.search(word.lowered) + if mo: + index = len(mo.group("denominator")) + return f"{self._plnoun(word[:index])}{word[index:]}" + + # handle units given in degrees (only accept if + # there is no more than one word following) + # degree Celsius => degrees Celsius but degree + # fahrenheit hour => degree fahrenheit hours + if len(word.split_) >= 2 and word.split_[-2] == "degree": + return " ".join([self._plnoun(word.first)] + word.split_[1:]) + + with contextlib.suppress(ValueError): + return self._handle_prepositional_phrase( + word.lowered, + functools.partial(self._plnoun, count=2), + '-', + ) + + # HANDLE PRONOUNS + + for k, v in pl_pron_acc_keys_bysize.items(): + if word.lowered[-k:] in v: # ends with accusative pronoun + for pk, pv in pl_prep_bysize.items(): + if word.lowered[:pk] in pv: # starts with a prep + if word.lowered.split() == [ + word.lowered[:pk], + word.lowered[-k:], + ]: + # only whitespace in between + return word.lowered[:-k] + pl_pron_acc[word.lowered[-k:]] + + try: + return pl_pron_nom[word.lowered] + except KeyError: + pass + + try: + return pl_pron_acc[word.lowered] + except KeyError: + pass + + # HANDLE ISOLATED IRREGULAR PLURALS + + if word.last in pl_sb_irregular_caps: + llen = len(word.last) + return f"{word[:-llen]}{pl_sb_irregular_caps[word.last]}" + + lowered_last = word.last.lower() + if lowered_last in pl_sb_irregular: + llen = len(lowered_last) + return f"{word[:-llen]}{pl_sb_irregular[lowered_last]}" + + dash_split = word.lowered.split('-') + if (" ".join(dash_split[-2:])).lower() in pl_sb_irregular_compound: + llen = len( + " ".join(dash_split[-2:]) + ) # TODO: what if 2 spaces between these words? + return ( + f"{word[:-llen]}" + f"{pl_sb_irregular_compound[(' '.join(dash_split[-2:])).lower()]}" + ) + + if word.lowered[-3:] == "quy": + return f"{word[:-1]}ies" + + if word.lowered[-6:] == "person": + if self.classical_dict["persons"]: + return f"{word}s" + else: + return f"{word[:-4]}ople" + + # HANDLE FAMILIES OF IRREGULAR PLURALS + + if word.lowered[-3:] == "man": + for k, v in pl_sb_U_man_mans_bysize.items(): + if word.lowered[-k:] in v: + return f"{word}s" + for k, v in pl_sb_U_man_mans_caps_bysize.items(): + if word[-k:] in v: + return f"{word}s" + return f"{word[:-3]}men" + if word.lowered[-5:] == "mouse": + return f"{word[:-5]}mice" + if word.lowered[-5:] == "louse": + v = pl_sb_U_louse_lice_bysize.get(len(word)) + if v and word.lowered in v: + return f"{word[:-5]}lice" + return f"{word}s" + if word.lowered[-5:] == "goose": + return f"{word[:-5]}geese" + if word.lowered[-5:] == "tooth": + return f"{word[:-5]}teeth" + if word.lowered[-4:] == "foot": + return f"{word[:-4]}feet" + if word.lowered[-4:] == "taco": + return f"{word[:-5]}tacos" + + if word.lowered == "die": + return "dice" + + # HANDLE UNASSIMILATED IMPORTS + + if word.lowered[-4:] == "ceps": + return word + if word.lowered[-4:] == "zoon": + return f"{word[:-2]}a" + if word.lowered[-3:] in ("cis", "sis", "xis"): + return f"{word[:-2]}es" + + for lastlet, d, numend, post in ( + ("h", pl_sb_U_ch_chs_bysize, None, "s"), + ("x", pl_sb_U_ex_ices_bysize, -2, "ices"), + ("x", pl_sb_U_ix_ices_bysize, -2, "ices"), + ("m", pl_sb_U_um_a_bysize, -2, "a"), + ("s", pl_sb_U_us_i_bysize, -2, "i"), + ("n", pl_sb_U_on_a_bysize, -2, "a"), + ("a", pl_sb_U_a_ae_bysize, None, "e"), + ): + if word.lowered[-1] == lastlet: # this test to add speed + for k, v in d.items(): + if word.lowered[-k:] in v: + return word[:numend] + post + + # HANDLE INCOMPLETELY ASSIMILATED IMPORTS + + if self.classical_dict["ancient"]: + if word.lowered[-4:] == "trix": + return f"{word[:-1]}ces" + if word.lowered[-3:] in ("eau", "ieu"): + return f"{word}x" + if word.lowered[-3:] in ("ynx", "inx", "anx") and len(word) > 4: + return f"{word[:-1]}ges" + + for lastlet, d, numend, post in ( + ("n", pl_sb_C_en_ina_bysize, -2, "ina"), + ("x", pl_sb_C_ex_ices_bysize, -2, "ices"), + ("x", pl_sb_C_ix_ices_bysize, -2, "ices"), + ("m", pl_sb_C_um_a_bysize, -2, "a"), + ("s", pl_sb_C_us_i_bysize, -2, "i"), + ("s", pl_sb_C_us_us_bysize, None, ""), + ("a", pl_sb_C_a_ae_bysize, None, "e"), + ("a", pl_sb_C_a_ata_bysize, None, "ta"), + ("s", pl_sb_C_is_ides_bysize, -1, "des"), + ("o", pl_sb_C_o_i_bysize, -1, "i"), + ("n", pl_sb_C_on_a_bysize, -2, "a"), + ): + if word.lowered[-1] == lastlet: # this test to add speed + for k, v in d.items(): + if word.lowered[-k:] in v: + return word[:numend] + post + + for d, numend, post in ( + (pl_sb_C_i_bysize, None, "i"), + (pl_sb_C_im_bysize, None, "im"), + ): + for k, v in d.items(): + if word.lowered[-k:] in v: + return word[:numend] + post + + # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS + + if lowered_last in pl_sb_singular_s_complete: + return f"{word}es" + + for k, v in pl_sb_singular_s_bysize.items(): + if word.lowered[-k:] in v: + return f"{word}es" + + if word.lowered[-2:] == "es" and word[0] == word[0].upper(): + return f"{word}es" + + if word.lowered[-1] == "z": + for k, v in pl_sb_z_zes_bysize.items(): + if word.lowered[-k:] in v: + return f"{word}es" + + if word.lowered[-2:-1] != "z": + return f"{word}zes" + + if word.lowered[-2:] == "ze": + for k, v in pl_sb_ze_zes_bysize.items(): + if word.lowered[-k:] in v: + return f"{word}s" + + if word.lowered[-2:] in ("ch", "sh", "zz", "ss") or word.lowered[-1] == "x": + return f"{word}es" + + # HANDLE ...f -> ...ves + + if word.lowered[-3:] in ("elf", "alf", "olf"): + return f"{word[:-1]}ves" + if word.lowered[-3:] == "eaf" and word.lowered[-4:-3] != "d": + return f"{word[:-1]}ves" + if word.lowered[-4:] in ("nife", "life", "wife"): + return f"{word[:-2]}ves" + if word.lowered[-3:] == "arf": + return f"{word[:-1]}ves" + + # HANDLE ...y + + if word.lowered[-1] == "y": + if word.lowered[-2:-1] in "aeiou" or len(word) == 1: + return f"{word}s" + + if self.classical_dict["names"]: + if word.lowered[-1] == "y" and word[0] == word[0].upper(): + return f"{word}s" + + return f"{word[:-1]}ies" + + # HANDLE ...o + + if lowered_last in pl_sb_U_o_os_complete: + return f"{word}s" + + for k, v in pl_sb_U_o_os_bysize.items(): + if word.lowered[-k:] in v: + return f"{word}s" + + if word.lowered[-2:] in ("ao", "eo", "io", "oo", "uo"): + return f"{word}s" + + if word.lowered[-1] == "o": + return f"{word}es" + + # OTHERWISE JUST ADD ...s + + return f"{word}s" + + @classmethod + def _handle_prepositional_phrase(cls, phrase, transform, sep): + """ + Given a word or phrase possibly separated by sep, parse out + the prepositional phrase and apply the transform to the word + preceding the prepositional phrase. + + Raise ValueError if the pivot is not found or if at least two + separators are not found. + + >>> engine._handle_prepositional_phrase("man-of-war", str.upper, '-') + 'MAN-of-war' + >>> engine._handle_prepositional_phrase("man of war", str.upper, ' ') + 'MAN of war' + """ + parts = phrase.split(sep) + if len(parts) < 3: + raise ValueError("Cannot handle words with fewer than two separators") + + pivot = cls._find_pivot(parts, pl_prep_list_da) + + transformed = transform(parts[pivot - 1]) or parts[pivot - 1] + return " ".join( + parts[: pivot - 1] + [sep.join([transformed, parts[pivot], ''])] + ) + " ".join(parts[(pivot + 1) :]) + + @staticmethod + def _find_pivot(words, candidates): + pivots = ( + index for index in range(1, len(words) - 1) if words[index] in candidates + ) + try: + return next(pivots) + except StopIteration: + raise ValueError("No pivot found") + + def _pl_special_verb( # noqa: C901 + self, word: str, count: Optional[Union[str, int]] = None + ) -> Union[str, bool]: + if self.classical_dict["zero"] and str(count).lower() in pl_count_zero: + return False + count = self.get_count(count) + + if count == 1: + return word + + # HANDLE USER-DEFINED VERBS + + value = self.ud_match(word, self.pl_v_user_defined) + if value is not None: + return value + + # HANDLE IRREGULAR PRESENT TENSE (SIMPLE AND COMPOUND) + + try: + words = Words(word) + except IndexError: + return False # word is '' + + if words.first in plverb_irregular_pres: + return f"{plverb_irregular_pres[words.first]}{words[len(words.first) :]}" + + # HANDLE IRREGULAR FUTURE, PRETERITE AND PERFECT TENSES + + if words.first in plverb_irregular_non_pres: + return word + + # HANDLE PRESENT NEGATIONS (SIMPLE AND COMPOUND) + + if words.first.endswith("n't") and words.first[:-3] in plverb_irregular_pres: + return ( + f"{plverb_irregular_pres[words.first[:-3]]}n't" + f"{words[len(words.first) :]}" + ) + + if words.first.endswith("n't"): + return word + + # HANDLE SPECIAL CASES + + mo = PLVERB_SPECIAL_S_RE.search(word) + if mo: + return False + if WHITESPACE.search(word): + return False + + if words.lowered == "quizzes": + return "quiz" + + # HANDLE STANDARD 3RD PERSON (CHOP THE ...(e)s OFF SINGLE WORDS) + + if ( + words.lowered[-4:] in ("ches", "shes", "zzes", "sses") + or words.lowered[-3:] == "xes" + ): + return words[:-2] + + if words.lowered[-3:] == "ies" and len(words) > 3: + return words.lowered[:-3] + "y" + + if ( + words.last.lower() in pl_v_oes_oe + or words.lowered[-4:] in pl_v_oes_oe_endings_size4 + or words.lowered[-5:] in pl_v_oes_oe_endings_size5 + ): + return words[:-1] + + if words.lowered.endswith("oes") and len(words) > 3: + return words.lowered[:-2] + + mo = ENDS_WITH_S.search(words) + if mo: + return mo.group(1) + + # OTHERWISE, A REGULAR VERB (HANDLE ELSEWHERE) + + return False + + def _pl_general_verb( + self, word: str, count: Optional[Union[str, int]] = None + ) -> str: + count = self.get_count(count) + + if count == 1: + return word + + # HANDLE AMBIGUOUS PRESENT TENSES (SIMPLE AND COMPOUND) + + mo = plverb_ambiguous_pres_keys.search(word) + if mo: + return f"{plverb_ambiguous_pres[mo.group(1).lower()]}{mo.group(2)}" + + # HANDLE AMBIGUOUS PRETERITE AND PERFECT TENSES + + mo = plverb_ambiguous_non_pres.search(word) + if mo: + return word + + # OTHERWISE, 1st OR 2ND PERSON IS UNINFLECTED + + return word + + def _pl_special_adjective( + self, word: str, count: Optional[Union[str, int]] = None + ) -> Union[str, bool]: + count = self.get_count(count) + + if count == 1: + return word + + # HANDLE USER-DEFINED ADJECTIVES + + value = self.ud_match(word, self.pl_adj_user_defined) + if value is not None: + return value + + # HANDLE KNOWN CASES + + mo = pl_adj_special_keys.search(word) + if mo: + return pl_adj_special[mo.group(1).lower()] + + # HANDLE POSSESSIVES + + mo = pl_adj_poss_keys.search(word) + if mo: + return pl_adj_poss[mo.group(1).lower()] + + mo = ENDS_WITH_APOSTROPHE_S.search(word) + if mo: + pl = self.plural_noun(mo.group(1)) + trailing_s = "" if pl[-1] == "s" else "s" + return f"{pl}'{trailing_s}" + + # OTHERWISE, NO IDEA + + return False + + # @profile + def _sinoun( # noqa: C901 + self, + word: str, + count: Optional[Union[str, int]] = None, + gender: Optional[str] = None, + ) -> Union[str, bool]: + count = self.get_count(count) + + # DEFAULT TO PLURAL + + if count == 2: + return word + + # SET THE GENDER + + try: + if gender is None: + gender = self.thegender + elif gender not in singular_pronoun_genders: + raise BadGenderError + except (TypeError, IndexError): + raise BadGenderError + + # HANDLE USER-DEFINED NOUNS + + value = self.ud_match(word, self.si_sb_user_defined) + if value is not None: + return value + + # HANDLE EMPTY WORD, SINGULAR COUNT AND UNINFLECTED PLURALS + + if word == "": + return word + + if word in si_sb_ois_oi_case: + return word[:-1] + + words = Words(word) + + if words.last.lower() in pl_sb_uninflected_complete: + return word + + if word in pl_sb_uninflected_caps: + return word + + for k, v in pl_sb_uninflected_bysize.items(): + if words.lowered[-k:] in v: + return word + + if self.classical_dict["herd"] and words.last.lower() in pl_sb_uninflected_herd: + return word + + if words.last.lower() in pl_sb_C_us_us: + return word if self.classical_dict["ancient"] else False + + # HANDLE COMPOUNDS ("Governor General", "mother-in-law", "aide-de-camp", ETC.) + + mo = PL_SB_POSTFIX_ADJ_STEMS_RE.search(word) + if mo and mo.group(2) != "": + return f"{self._sinoun(mo.group(1), 1, gender=gender)}{mo.group(2)}" + + with contextlib.suppress(ValueError): + return self._handle_prepositional_phrase( + words.lowered, + functools.partial(self._sinoun, count=1, gender=gender), + ' ', + ) + + with contextlib.suppress(ValueError): + return self._handle_prepositional_phrase( + words.lowered, + functools.partial(self._sinoun, count=1, gender=gender), + '-', + ) + + # HANDLE PRONOUNS + + for k, v in si_pron_acc_keys_bysize.items(): + if words.lowered[-k:] in v: # ends with accusative pronoun + for pk, pv in pl_prep_bysize.items(): + if words.lowered[:pk] in pv: # starts with a prep + if words.lowered.split() == [ + words.lowered[:pk], + words.lowered[-k:], + ]: + # only whitespace in between + return words.lowered[:-k] + get_si_pron( + "acc", words.lowered[-k:], gender + ) + + try: + return get_si_pron("nom", words.lowered, gender) + except KeyError: + pass + + try: + return get_si_pron("acc", words.lowered, gender) + except KeyError: + pass + + # HANDLE ISOLATED IRREGULAR PLURALS + + if words.last in si_sb_irregular_caps: + llen = len(words.last) + return "{}{}".format(word[:-llen], si_sb_irregular_caps[words.last]) + + if words.last.lower() in si_sb_irregular: + llen = len(words.last.lower()) + return "{}{}".format(word[:-llen], si_sb_irregular[words.last.lower()]) + + dash_split = words.lowered.split("-") + if (" ".join(dash_split[-2:])).lower() in si_sb_irregular_compound: + llen = len( + " ".join(dash_split[-2:]) + ) # TODO: what if 2 spaces between these words? + return "{}{}".format( + word[:-llen], + si_sb_irregular_compound[(" ".join(dash_split[-2:])).lower()], + ) + + if words.lowered[-5:] == "quies": + return word[:-3] + "y" + + if words.lowered[-7:] == "persons": + return word[:-1] + if words.lowered[-6:] == "people": + return word[:-4] + "rson" + + # HANDLE FAMILIES OF IRREGULAR PLURALS + + if words.lowered[-4:] == "mans": + for k, v in si_sb_U_man_mans_bysize.items(): + if words.lowered[-k:] in v: + return word[:-1] + for k, v in si_sb_U_man_mans_caps_bysize.items(): + if word[-k:] in v: + return word[:-1] + if words.lowered[-3:] == "men": + return word[:-3] + "man" + if words.lowered[-4:] == "mice": + return word[:-4] + "mouse" + if words.lowered[-4:] == "lice": + v = si_sb_U_louse_lice_bysize.get(len(word)) + if v and words.lowered in v: + return word[:-4] + "louse" + if words.lowered[-5:] == "geese": + return word[:-5] + "goose" + if words.lowered[-5:] == "teeth": + return word[:-5] + "tooth" + if words.lowered[-4:] == "feet": + return word[:-4] + "foot" + + if words.lowered == "dice": + return "die" + + # HANDLE UNASSIMILATED IMPORTS + + if words.lowered[-4:] == "ceps": + return word + if words.lowered[-3:] == "zoa": + return word[:-1] + "on" + + for lastlet, d, unass_numend, post in ( + ("s", si_sb_U_ch_chs_bysize, -1, ""), + ("s", si_sb_U_ex_ices_bysize, -4, "ex"), + ("s", si_sb_U_ix_ices_bysize, -4, "ix"), + ("a", si_sb_U_um_a_bysize, -1, "um"), + ("i", si_sb_U_us_i_bysize, -1, "us"), + ("a", si_sb_U_on_a_bysize, -1, "on"), + ("e", si_sb_U_a_ae_bysize, -1, ""), + ): + if words.lowered[-1] == lastlet: # this test to add speed + for k, v in d.items(): + if words.lowered[-k:] in v: + return word[:unass_numend] + post + + # HANDLE INCOMPLETELY ASSIMILATED IMPORTS + + if self.classical_dict["ancient"]: + + if words.lowered[-6:] == "trices": + return word[:-3] + "x" + if words.lowered[-4:] in ("eaux", "ieux"): + return word[:-1] + if words.lowered[-5:] in ("ynges", "inges", "anges") and len(word) > 6: + return word[:-3] + "x" + + for lastlet, d, class_numend, post in ( + ("a", si_sb_C_en_ina_bysize, -3, "en"), + ("s", si_sb_C_ex_ices_bysize, -4, "ex"), + ("s", si_sb_C_ix_ices_bysize, -4, "ix"), + ("a", si_sb_C_um_a_bysize, -1, "um"), + ("i", si_sb_C_us_i_bysize, -1, "us"), + ("s", pl_sb_C_us_us_bysize, None, ""), + ("e", si_sb_C_a_ae_bysize, -1, ""), + ("a", si_sb_C_a_ata_bysize, -2, ""), + ("s", si_sb_C_is_ides_bysize, -3, "s"), + ("i", si_sb_C_o_i_bysize, -1, "o"), + ("a", si_sb_C_on_a_bysize, -1, "on"), + ("m", si_sb_C_im_bysize, -2, ""), + ("i", si_sb_C_i_bysize, -1, ""), + ): + if words.lowered[-1] == lastlet: # this test to add speed + for k, v in d.items(): + if words.lowered[-k:] in v: + return word[:class_numend] + post + + # HANDLE PLURLS ENDING IN uses -> use + + if ( + words.lowered[-6:] == "houses" + or word in si_sb_uses_use_case + or words.last.lower() in si_sb_uses_use + ): + return word[:-1] + + # HANDLE PLURLS ENDING IN ies -> ie + + if word in si_sb_ies_ie_case or words.last.lower() in si_sb_ies_ie: + return word[:-1] + + # HANDLE PLURLS ENDING IN oes -> oe + + if ( + words.lowered[-5:] == "shoes" + or word in si_sb_oes_oe_case + or words.last.lower() in si_sb_oes_oe + ): + return word[:-1] + + # HANDLE SINGULAR NOUNS ENDING IN ...s OR OTHER SILIBANTS + + if word in si_sb_sses_sse_case or words.last.lower() in si_sb_sses_sse: + return word[:-1] + + if words.last.lower() in si_sb_singular_s_complete: + return word[:-2] + + for k, v in si_sb_singular_s_bysize.items(): + if words.lowered[-k:] in v: + return word[:-2] + + if words.lowered[-4:] == "eses" and word[0] == word[0].upper(): + return word[:-2] + + if words.last.lower() in si_sb_z_zes: + return word[:-2] + + if words.last.lower() in si_sb_zzes_zz: + return word[:-2] + + if words.lowered[-4:] == "zzes": + return word[:-3] + + if word in si_sb_ches_che_case or words.last.lower() in si_sb_ches_che: + return word[:-1] + + if words.lowered[-4:] in ("ches", "shes"): + return word[:-2] + + if words.last.lower() in si_sb_xes_xe: + return word[:-1] + + if words.lowered[-3:] == "xes": + return word[:-2] + + # HANDLE ...f -> ...ves + + if word in si_sb_ves_ve_case or words.last.lower() in si_sb_ves_ve: + return word[:-1] + + if words.lowered[-3:] == "ves": + if words.lowered[-5:-3] in ("el", "al", "ol"): + return word[:-3] + "f" + if words.lowered[-5:-3] == "ea" and word[-6:-5] != "d": + return word[:-3] + "f" + if words.lowered[-5:-3] in ("ni", "li", "wi"): + return word[:-3] + "fe" + if words.lowered[-5:-3] == "ar": + return word[:-3] + "f" + + # HANDLE ...y + + if words.lowered[-2:] == "ys": + if len(words.lowered) > 2 and words.lowered[-3] in "aeiou": + return word[:-1] + + if self.classical_dict["names"]: + if words.lowered[-2:] == "ys" and word[0] == word[0].upper(): + return word[:-1] + + if words.lowered[-3:] == "ies": + return word[:-3] + "y" + + # HANDLE ...o + + if words.lowered[-2:] == "os": + + if words.last.lower() in si_sb_U_o_os_complete: + return word[:-1] + + for k, v in si_sb_U_o_os_bysize.items(): + if words.lowered[-k:] in v: + return word[:-1] + + if words.lowered[-3:] in ("aos", "eos", "ios", "oos", "uos"): + return word[:-1] + + if words.lowered[-3:] == "oes": + return word[:-2] + + # UNASSIMILATED IMPORTS FINAL RULE + + if word in si_sb_es_is: + return word[:-2] + "is" + + # OTHERWISE JUST REMOVE ...s + + if words.lowered[-1] == "s": + return word[:-1] + + # COULD NOT FIND SINGULAR + + return False + + # ADJECTIVES + + @validate_arguments + def a(self, text: Word, count: Optional[Union[int, str, Any]] = 1) -> str: + """ + Return the appropriate indefinite article followed by text. + + The indefinite article is either 'a' or 'an'. + + If count is not one, then return count followed by text + instead of 'a' or 'an'. + + Whitespace at the start and end is preserved. + + """ + mo = INDEFINITE_ARTICLE_TEST.search(text) + if mo: + word = mo.group(2) + if not word: + return text + pre = mo.group(1) + post = mo.group(3) + result = self._indef_article(word, count) + return f"{pre}{result}{post}" + return "" + + an = a + + _indef_article_cases = ( + # HANDLE ORDINAL FORMS + (A_ordinal_a, "a"), + (A_ordinal_an, "an"), + # HANDLE SPECIAL CASES + (A_explicit_an, "an"), + (SPECIAL_AN, "an"), + (SPECIAL_A, "a"), + # HANDLE ABBREVIATIONS + (A_abbrev, "an"), + (SPECIAL_ABBREV_AN, "an"), + (SPECIAL_ABBREV_A, "a"), + # HANDLE CONSONANTS + (CONSONANTS, "a"), + # HANDLE SPECIAL VOWEL-FORMS + (ARTICLE_SPECIAL_EU, "a"), + (ARTICLE_SPECIAL_ONCE, "a"), + (ARTICLE_SPECIAL_ONETIME, "a"), + (ARTICLE_SPECIAL_UNIT, "a"), + (ARTICLE_SPECIAL_UBA, "a"), + (ARTICLE_SPECIAL_UKR, "a"), + (A_explicit_a, "a"), + # HANDLE SPECIAL CAPITALS + (SPECIAL_CAPITALS, "a"), + # HANDLE VOWELS + (VOWELS, "an"), + # HANDLE y... + # (BEFORE CERTAIN CONSONANTS IMPLIES (UNNATURALIZED) "i.." SOUND) + (A_y_cons, "an"), + ) + + def _indef_article(self, word: str, count: Union[int, str, Any]) -> str: + mycount = self.get_count(count) + + if mycount != 1: + return f"{count} {word}" + + # HANDLE USER-DEFINED VARIANTS + + value = self.ud_match(word, self.A_a_user_defined) + if value is not None: + return f"{value} {word}" + + matches = ( + f'{article} {word}' + for regexen, article in self._indef_article_cases + if regexen.search(word) + ) + + # OTHERWISE, GUESS "a" + fallback = f'a {word}' + return next(matches, fallback) + + # 2. TRANSLATE ZERO-QUANTIFIED $word TO "no plural($word)" + + @validate_arguments + def no(self, text: Word, count: Optional[Union[int, str]] = None) -> str: + """ + If count is 0, no, zero or nil, return 'no' followed by the plural + of text. + + If count is one of: + 1, a, an, one, each, every, this, that + return count followed by text. + + Otherwise return count follow by the plural of text. + + In the return value count is always followed by a space. + + Whitespace at the start and end is preserved. + + """ + if count is None and self.persistent_count is not None: + count = self.persistent_count + + if count is None: + count = 0 + mo = PARTITION_WORD.search(text) + if mo: + pre = mo.group(1) + word = mo.group(2) + post = mo.group(3) + else: + pre = "" + word = "" + post = "" + + if str(count).lower() in pl_count_zero: + count = 'no' + return f"{pre}{count} {self.plural(word, count)}{post}" + + # PARTICIPLES + + @validate_arguments + def present_participle(self, word: Word) -> str: + """ + Return the present participle for word. + + word is the 3rd person singular verb. + + """ + plv = self.plural_verb(word, 2) + ans = plv + + for regexen, repl in PRESENT_PARTICIPLE_REPLACEMENTS: + ans, num = regexen.subn(repl, plv) + if num: + return f"{ans}ing" + return f"{ans}ing" + + # NUMERICAL INFLECTIONS + + @validate_arguments + def ordinal(self, num: Union[int, Word]) -> str: # noqa: C901 + """ + Return the ordinal of num. + + num can be an integer or text + + e.g. ordinal(1) returns '1st' + ordinal('one') returns 'first' + + """ + if DIGIT.match(str(num)): + if isinstance(num, (int, float)): + n = int(num) + else: + if "." in str(num): + try: + # numbers after decimal, + # so only need last one for ordinal + n = int(num[-1]) + + except ValueError: # ends with '.', so need to use whole string + n = int(num[:-1]) + else: + n = int(num) + try: + post = nth[n % 100] + except KeyError: + post = nth[n % 10] + return f"{num}{post}" + else: + # Mad props to Damian Conway (?) whose ordinal() + # algorithm is type-bendy enough to foil MyPy + str_num: str = num # type: ignore[assignment] + mo = ordinal_suff.search(str_num) + if mo: + post = ordinal[mo.group(1)] + rval = ordinal_suff.sub(post, str_num) + else: + rval = f"{str_num}th" + return rval + + def millfn(self, ind: int = 0) -> str: + if ind > len(mill) - 1: + print3("number out of range") + raise NumOutOfRangeError + return mill[ind] + + def unitfn(self, units: int, mindex: int = 0) -> str: + return f"{unit[units]}{self.millfn(mindex)}" + + def tenfn(self, tens, units, mindex=0) -> str: + if tens != 1: + tens_part = ten[tens] + if tens and units: + hyphen = "-" + else: + hyphen = "" + unit_part = unit[units] + mill_part = self.millfn(mindex) + return f"{tens_part}{hyphen}{unit_part}{mill_part}" + return f"{teen[units]}{mill[mindex]}" + + def hundfn(self, hundreds: int, tens: int, units: int, mindex: int) -> str: + if hundreds: + andword = f" {self._number_args['andword']} " if tens or units else "" + # use unit not unitfn as simpler + return ( + f"{unit[hundreds]} hundred{andword}" + f"{self.tenfn(tens, units)}{self.millfn(mindex)}, " + ) + if tens or units: + return f"{self.tenfn(tens, units)}{self.millfn(mindex)}, " + return "" + + def group1sub(self, mo: Match) -> str: + units = int(mo.group(1)) + if units == 1: + return f" {self._number_args['one']}, " + elif units: + return f"{unit[units]}, " + else: + return f" {self._number_args['zero']}, " + + def group1bsub(self, mo: Match) -> str: + units = int(mo.group(1)) + if units: + return f"{unit[units]}, " + else: + return f" {self._number_args['zero']}, " + + def group2sub(self, mo: Match) -> str: + tens = int(mo.group(1)) + units = int(mo.group(2)) + if tens: + return f"{self.tenfn(tens, units)}, " + if units: + return f" {self._number_args['zero']} {unit[units]}, " + return f" {self._number_args['zero']} {self._number_args['zero']}, " + + def group3sub(self, mo: Match) -> str: + hundreds = int(mo.group(1)) + tens = int(mo.group(2)) + units = int(mo.group(3)) + if hundreds == 1: + hunword = f" {self._number_args['one']}" + elif hundreds: + hunword = str(unit[hundreds]) + else: + hunword = f" {self._number_args['zero']}" + if tens: + tenword = self.tenfn(tens, units) + elif units: + tenword = f" {self._number_args['zero']} {unit[units]}" + else: + tenword = f" {self._number_args['zero']} {self._number_args['zero']}" + return f"{hunword} {tenword}, " + + def hundsub(self, mo: Match) -> str: + ret = self.hundfn( + int(mo.group(1)), int(mo.group(2)), int(mo.group(3)), self.mill_count + ) + self.mill_count += 1 + return ret + + def tensub(self, mo: Match) -> str: + return f"{self.tenfn(int(mo.group(1)), int(mo.group(2)), self.mill_count)}, " + + def unitsub(self, mo: Match) -> str: + return f"{self.unitfn(int(mo.group(1)), self.mill_count)}, " + + def enword(self, num: str, group: int) -> str: + # import pdb + # pdb.set_trace() + + if group == 1: + num = DIGIT_GROUP.sub(self.group1sub, num) + elif group == 2: + num = TWO_DIGITS.sub(self.group2sub, num) + num = DIGIT_GROUP.sub(self.group1bsub, num, 1) + elif group == 3: + num = THREE_DIGITS.sub(self.group3sub, num) + num = TWO_DIGITS.sub(self.group2sub, num, 1) + num = DIGIT_GROUP.sub(self.group1sub, num, 1) + elif int(num) == 0: + num = self._number_args["zero"] + elif int(num) == 1: + num = self._number_args["one"] + else: + num = num.lstrip().lstrip("0") + self.mill_count = 0 + # surely there's a better way to do the next bit + mo = THREE_DIGITS_WORD.search(num) + while mo: + num = THREE_DIGITS_WORD.sub(self.hundsub, num, 1) + mo = THREE_DIGITS_WORD.search(num) + num = TWO_DIGITS_WORD.sub(self.tensub, num, 1) + num = ONE_DIGIT_WORD.sub(self.unitsub, num, 1) + return num + + @validate_arguments(config=dict(arbitrary_types_allowed=True)) # noqa: C901 + def number_to_words( # noqa: C901 + self, + num: Union[Number, Word], + wantlist: bool = False, + group: int = 0, + comma: Union[Falsish, str] = ",", + andword: str = "and", + zero: str = "zero", + one: str = "one", + decimal: Union[Falsish, str] = "point", + threshold: Optional[int] = None, + ) -> Union[str, List[str]]: + """ + Return a number in words. + + group = 1, 2 or 3 to group numbers before turning into words + comma: define comma + + andword: + word for 'and'. Can be set to ''. + e.g. "one hundred and one" vs "one hundred one" + + zero: word for '0' + one: word for '1' + decimal: word for decimal point + threshold: numbers above threshold not turned into words + + parameters not remembered from last call. Departure from Perl version. + """ + self._number_args = {"andword": andword, "zero": zero, "one": one} + num = str(num) + + # Handle "stylistic" conversions (up to a given threshold)... + if threshold is not None and float(num) > threshold: + spnum = num.split(".", 1) + while comma: + (spnum[0], n) = FOUR_DIGIT_COMMA.subn(r"\1,\2", spnum[0]) + if n == 0: + break + try: + return f"{spnum[0]}.{spnum[1]}" + except IndexError: + return str(spnum[0]) + + if group < 0 or group > 3: + raise BadChunkingOptionError + nowhite = num.lstrip() + if nowhite[0] == "+": + sign = "plus" + elif nowhite[0] == "-": + sign = "minus" + else: + sign = "" + + if num in nth_suff: + num = zero + + myord = num[-2:] in nth_suff + if myord: + num = num[:-2] + finalpoint = False + if decimal: + if group != 0: + chunks = num.split(".") + else: + chunks = num.split(".", 1) + if chunks[-1] == "": # remove blank string if nothing after decimal + chunks = chunks[:-1] + finalpoint = True # add 'point' to end of output + else: + chunks = [num] + + first: Union[int, str, bool] = 1 + loopstart = 0 + + if chunks[0] == "": + first = 0 + if len(chunks) > 1: + loopstart = 1 + + for i in range(loopstart, len(chunks)): + chunk = chunks[i] + # remove all non numeric \D + chunk = NON_DIGIT.sub("", chunk) + if chunk == "": + chunk = "0" + + if group == 0 and (first == 0 or first == ""): + chunk = self.enword(chunk, 1) + else: + chunk = self.enword(chunk, group) + + if chunk[-2:] == ", ": + chunk = chunk[:-2] + chunk = WHITESPACES_COMMA.sub(",", chunk) + + if group == 0 and first: + chunk = COMMA_WORD.sub(f" {andword} \\1", chunk) + chunk = WHITESPACES.sub(" ", chunk) + # chunk = re.sub(r"(\A\s|\s\Z)", self.blankfn, chunk) + chunk = chunk.strip() + if first: + first = "" + chunks[i] = chunk + + numchunks = [] + if first != 0: + numchunks = chunks[0].split(f"{comma} ") + + if myord and numchunks: + # TODO: can this be just one re as it is in perl? + mo = ordinal_suff.search(numchunks[-1]) + if mo: + numchunks[-1] = ordinal_suff.sub(ordinal[mo.group(1)], numchunks[-1]) + else: + numchunks[-1] += "th" + + for chunk in chunks[1:]: + numchunks.append(decimal) + numchunks.extend(chunk.split(f"{comma} ")) + + if finalpoint: + numchunks.append(decimal) + + # wantlist: Perl list context. can explicitly specify in Python + if wantlist: + if sign: + numchunks = [sign] + numchunks + return numchunks + elif group: + signout = f"{sign} " if sign else "" + return f"{signout}{', '.join(numchunks)}" + else: + signout = f"{sign} " if sign else "" + num = f"{signout}{numchunks.pop(0)}" + if decimal is None: + first = True + else: + first = not num.endswith(decimal) + for nc in numchunks: + if nc == decimal: + num += f" {nc}" + first = 0 + elif first: + num += f"{comma} {nc}" + else: + num += f" {nc}" + return num + + # Join words with commas and a trailing 'and' (when appropriate)... + + @validate_arguments + def join( + self, + words: Optional[Sequence[Word]], + sep: Optional[str] = None, + sep_spaced: bool = True, + final_sep: Optional[str] = None, + conj: str = "and", + conj_spaced: bool = True, + ) -> str: + """ + Join words into a list. + + e.g. join(['ant', 'bee', 'fly']) returns 'ant, bee, and fly' + + options: + conj: replacement for 'and' + sep: separator. default ',', unless ',' is in the list then ';' + final_sep: final separator. default ',', unless ',' is in the list then ';' + conj_spaced: boolean. Should conj have spaces around it + + """ + if not words: + return "" + if len(words) == 1: + return words[0] + + if conj_spaced: + if conj == "": + conj = " " + else: + conj = f" {conj} " + + if len(words) == 2: + return f"{words[0]}{conj}{words[1]}" + + if sep is None: + if "," in "".join(words): + sep = ";" + else: + sep = "," + if final_sep is None: + final_sep = sep + + final_sep = f"{final_sep}{conj}" + + if sep_spaced: + sep += " " + + return f"{sep.join(words[0:-1])}{final_sep}{words[-1]}" diff --git a/libs/win/inflect/py.typed b/libs/win/inflect/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/jaraco.classes-1.5-py3.6-nspkg.pth b/libs/win/jaraco.classes-1.5-py3.6-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.classes-1.5-py3.6-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.collections-1.6.0-py3.7-nspkg.pth b/libs/win/jaraco.collections-1.6.0-py3.7-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.collections-1.6.0-py3.7-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.functools-1.20-py3.6-nspkg.pth b/libs/win/jaraco.functools-1.20-py3.6-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.functools-1.20-py3.6-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.structures-1.1.2-py3.6-nspkg.pth b/libs/win/jaraco.structures-1.1.2-py3.6-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.structures-1.1.2-py3.6-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.text-1.10.1-py3.6-nspkg.pth b/libs/win/jaraco.text-1.10.1-py3.6-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.text-1.10.1-py3.6-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.ui-1.6-py3.6-nspkg.pth b/libs/win/jaraco.ui-1.6-py3.6-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.ui-1.6-py3.6-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco.windows-3.9.2-py3.7-nspkg.pth b/libs/win/jaraco.windows-3.9.2-py3.7-nspkg.pth deleted file mode 100644 index 61cb14f9..00000000 --- a/libs/win/jaraco.windows-3.9.2-py3.7-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('jaraco',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('jaraco', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('jaraco', [os.path.dirname(p)])));m = m or sys.modules.setdefault('jaraco', types.ModuleType('jaraco'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/libs/win/jaraco/classes/ancestry.py b/libs/win/jaraco/classes/ancestry.py index 040ce612..dd9b2e92 100644 --- a/libs/win/jaraco/classes/ancestry.py +++ b/libs/win/jaraco/classes/ancestry.py @@ -3,73 +3,66 @@ Routines for obtaining the class names of an object and its parent classes. """ -from __future__ import unicode_literals +from more_itertools import unique_everseen def all_bases(c): - """ - return a tuple of all base classes the class c has as a parent. - >>> object in all_bases(list) - True - """ - return c.mro()[1:] + """ + return a tuple of all base classes the class c has as a parent. + >>> object in all_bases(list) + True + """ + return c.mro()[1:] def all_classes(c): - """ - return a tuple of all classes to which c belongs - >>> list in all_classes(list) - True - """ - return c.mro() + """ + return a tuple of all classes to which c belongs + >>> list in all_classes(list) + True + """ + return c.mro() + # borrowed from # http://code.activestate.com/recipes/576949-find-all-subclasses-of-a-given-class/ -def iter_subclasses(cls, _seen=None): - """ - Generator over all subclasses of a given class, in depth-first order. +def iter_subclasses(cls): + """ + Generator over all subclasses of a given class, in depth-first order. - >>> bool in list(iter_subclasses(int)) - True - >>> class A(object): pass - >>> class B(A): pass - >>> class C(A): pass - >>> class D(B,C): pass - >>> class E(D): pass - >>> - >>> for cls in iter_subclasses(A): - ... print(cls.__name__) - B - D - E - C - >>> # get ALL (new-style) classes currently defined - >>> res = [cls.__name__ for cls in iter_subclasses(object)] - >>> 'type' in res - True - >>> 'tuple' in res - True - >>> len(res) > 100 - True - """ + >>> bool in list(iter_subclasses(int)) + True + >>> class A(object): pass + >>> class B(A): pass + >>> class C(A): pass + >>> class D(B,C): pass + >>> class E(D): pass + >>> + >>> for cls in iter_subclasses(A): + ... print(cls.__name__) + B + D + E + C + >>> # get ALL classes currently defined + >>> res = [cls.__name__ for cls in iter_subclasses(object)] + >>> 'type' in res + True + >>> 'tuple' in res + True + >>> len(res) > 100 + True + """ + return unique_everseen(_iter_all_subclasses(cls)) - if not isinstance(cls, type): - raise TypeError( - 'iter_subclasses must be called with ' - 'new-style classes, not %.100r' % cls - ) - if _seen is None: - _seen = set() - try: - subs = cls.__subclasses__() - except TypeError: # fails only when cls is type - subs = cls.__subclasses__(cls) - for sub in subs: - if sub in _seen: - continue - _seen.add(sub) - yield sub - for sub in iter_subclasses(sub, _seen): - yield sub + +def _iter_all_subclasses(cls): + try: + subs = cls.__subclasses__() + except TypeError: # fails only when cls is type + subs = cls.__subclasses__(cls) + for sub in subs: + yield sub + yield from iter_subclasses(sub) diff --git a/libs/win/jaraco/classes/meta.py b/libs/win/jaraco/classes/meta.py index c26f7dc2..bd41a1d9 100644 --- a/libs/win/jaraco/classes/meta.py +++ b/libs/win/jaraco/classes/meta.py @@ -4,38 +4,63 @@ meta.py Some useful metaclasses. """ -from __future__ import unicode_literals - class LeafClassesMeta(type): - """ - A metaclass for classes that keeps track of all of them that - aren't base classes. - """ + """ + A metaclass for classes that keeps track of all of them that + aren't base classes. - _leaf_classes = set() + >>> Parent = LeafClassesMeta('MyParentClass', (), {}) + >>> Parent in Parent._leaf_classes + True + >>> Child = LeafClassesMeta('MyChildClass', (Parent,), {}) + >>> Child in Parent._leaf_classes + True + >>> Parent in Parent._leaf_classes + False - def __init__(cls, name, bases, attrs): - if not hasattr(cls, '_leaf_classes'): - cls._leaf_classes = set() - leaf_classes = getattr(cls, '_leaf_classes') - leaf_classes.add(cls) - # remove any base classes - leaf_classes -= set(bases) + >>> Other = LeafClassesMeta('OtherClass', (), {}) + >>> Parent in Other._leaf_classes + False + >>> len(Other._leaf_classes) + 1 + """ + + def __init__(cls, name, bases, attrs): + if not hasattr(cls, '_leaf_classes'): + cls._leaf_classes = set() + leaf_classes = getattr(cls, '_leaf_classes') + leaf_classes.add(cls) + # remove any base classes + leaf_classes -= set(bases) class TagRegistered(type): - """ - As classes of this metaclass are created, they keep a registry in the - base class of all classes by a class attribute, indicated by attr_name. - """ - attr_name = 'tag' + """ + As classes of this metaclass are created, they keep a registry in the + base class of all classes by a class attribute, indicated by attr_name. - def __init__(cls, name, bases, namespace): - super(TagRegistered, cls).__init__(name, bases, namespace) - if not hasattr(cls, '_registry'): - cls._registry = {} - meta = cls.__class__ - attr = getattr(cls, meta.attr_name, None) - if attr: - cls._registry[attr] = cls + >>> FooObject = TagRegistered('FooObject', (), dict(tag='foo')) + >>> FooObject._registry['foo'] is FooObject + True + >>> BarObject = TagRegistered('Barobject', (FooObject,), dict(tag='bar')) + >>> FooObject._registry is BarObject._registry + True + >>> len(FooObject._registry) + 2 + + '...' below should be 'jaraco.classes' but for pytest-dev/pytest#3396 + >>> FooObject._registry['bar'] + + """ + + attr_name = 'tag' + + def __init__(cls, name, bases, namespace): + super(TagRegistered, cls).__init__(name, bases, namespace) + if not hasattr(cls, '_registry'): + cls._registry = {} + meta = cls.__class__ + attr = getattr(cls, meta.attr_name, None) + if attr: + cls._registry[attr] = cls diff --git a/libs/win/jaraco/classes/properties.py b/libs/win/jaraco/classes/properties.py index 57f9054f..62f9e200 100644 --- a/libs/win/jaraco/classes/properties.py +++ b/libs/win/jaraco/classes/properties.py @@ -1,67 +1,170 @@ -from __future__ import unicode_literals - -import six - -__metaclass__ = type - - class NonDataProperty: - """Much like the property builtin, but only implements __get__, - making it a non-data property, and can be subsequently reset. + """Much like the property builtin, but only implements __get__, + making it a non-data property, and can be subsequently reset. - See http://users.rcn.com/python/download/Descriptor.htm for more - information. + See http://users.rcn.com/python/download/Descriptor.htm for more + information. - >>> class X(object): - ... @NonDataProperty - ... def foo(self): - ... return 3 - >>> x = X() - >>> x.foo - 3 - >>> x.foo = 4 - >>> x.foo - 4 - """ + >>> class X(object): + ... @NonDataProperty + ... def foo(self): + ... return 3 + >>> x = X() + >>> x.foo + 3 + >>> x.foo = 4 + >>> x.foo + 4 - def __init__(self, fget): - assert fget is not None, "fget cannot be none" - assert six.callable(fget), "fget must be callable" - self.fget = fget + '...' below should be 'jaraco.classes' but for pytest-dev/pytest#3396 + >>> X.foo + <....properties.NonDataProperty object at ...> + """ - def __get__(self, obj, objtype=None): - if obj is None: - return self - return self.fget(obj) + def __init__(self, fget): + assert fget is not None, "fget cannot be none" + assert callable(fget), "fget must be callable" + self.fget = fget + + def __get__(self, obj, objtype=None): + if obj is None: + return self + return self.fget(obj) -# from http://stackoverflow.com/a/5191224 -class ClassPropertyDescriptor: - - def __init__(self, fget, fset=None): - self.fget = fget - self.fset = fset - - def __get__(self, obj, klass=None): - if klass is None: - klass = type(obj) - return self.fget.__get__(obj, klass)() - - def __set__(self, obj, value): - if not self.fset: - raise AttributeError("can't set attribute") - type_ = type(obj) - return self.fset.__get__(obj, type_)(value) - - def setter(self, func): - if not isinstance(func, (classmethod, staticmethod)): - func = classmethod(func) - self.fset = func - return self +class classproperty: + """ + Like @property but applies at the class level. -def classproperty(func): - if not isinstance(func, (classmethod, staticmethod)): - func = classmethod(func) + >>> class X(metaclass=classproperty.Meta): + ... val = None + ... @classproperty + ... def foo(cls): + ... return cls.val + ... @foo.setter + ... def foo(cls, val): + ... cls.val = val + >>> X.foo + >>> X.foo = 3 + >>> X.foo + 3 + >>> x = X() + >>> x.foo + 3 + >>> X.foo = 4 + >>> x.foo + 4 - return ClassPropertyDescriptor(func) + Setting the property on an instance affects the class. + + >>> x.foo = 5 + >>> x.foo + 5 + >>> X.foo + 5 + >>> vars(x) + {} + >>> X().foo + 5 + + Attempting to set an attribute where no setter was defined + results in an AttributeError: + + >>> class GetOnly(metaclass=classproperty.Meta): + ... @classproperty + ... def foo(cls): + ... return 'bar' + >>> GetOnly.foo = 3 + Traceback (most recent call last): + ... + AttributeError: can't set attribute + + It is also possible to wrap a classmethod or staticmethod in + a classproperty. + + >>> class Static(metaclass=classproperty.Meta): + ... @classproperty + ... @classmethod + ... def foo(cls): + ... return 'foo' + ... @classproperty + ... @staticmethod + ... def bar(): + ... return 'bar' + >>> Static.foo + 'foo' + >>> Static.bar + 'bar' + + *Legacy* + + For compatibility, if the metaclass isn't specified, the + legacy behavior will be invoked. + + >>> class X: + ... val = None + ... @classproperty + ... def foo(cls): + ... return cls.val + ... @foo.setter + ... def foo(cls, val): + ... cls.val = val + >>> X.foo + >>> X.foo = 3 + >>> X.foo + 3 + >>> x = X() + >>> x.foo + 3 + >>> X.foo = 4 + >>> x.foo + 4 + + Note, because the metaclass was not specified, setting + a value on an instance does not have the intended effect. + + >>> x.foo = 5 + >>> x.foo + 5 + >>> X.foo # should be 5 + 4 + >>> vars(x) # should be empty + {'foo': 5} + >>> X().foo # should be 5 + 4 + """ + + class Meta(type): + def __setattr__(self, key, value): + obj = self.__dict__.get(key, None) + if type(obj) is classproperty: + return obj.__set__(self, value) + return super().__setattr__(key, value) + + def __init__(self, fget, fset=None): + self.fget = self._ensure_method(fget) + self.fset = fset + fset and self.setter(fset) + + def __get__(self, instance, owner=None): + return self.fget.__get__(None, owner)() + + def __set__(self, owner, value): + if not self.fset: + raise AttributeError("can't set attribute") + if type(owner) is not classproperty.Meta: + owner = type(owner) + return self.fset.__get__(None, owner)(value) + + def setter(self, fset): + self.fset = self._ensure_method(fset) + return self + + @classmethod + def _ensure_method(cls, fn): + """ + Ensure fn is a classmethod or staticmethod. + """ + needs_method = not isinstance(fn, (classmethod, staticmethod)) + return classmethod(fn) if needs_method else fn diff --git a/libs/win/jaraco/collections.py b/libs/win/jaraco/collections.py index bb463deb..db89b122 100644 --- a/libs/win/jaraco/collections.py +++ b/libs/win/jaraco/collections.py @@ -1,906 +1,1090 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, unicode_literals, division - import re import operator -import collections +import collections.abc import itertools import copy import functools +import random -try: - import collections.abc -except ImportError: - # Python 2.7 - collections.abc = collections - -import six from jaraco.classes.properties import NonDataProperty import jaraco.text class Projection(collections.abc.Mapping): - """ - Project a set of keys over a mapping + """ + Project a set of keys over a mapping - >>> sample = {'a': 1, 'b': 2, 'c': 3} - >>> prj = Projection(['a', 'c', 'd'], sample) - >>> prj == {'a': 1, 'c': 3} - True + >>> sample = {'a': 1, 'b': 2, 'c': 3} + >>> prj = Projection(['a', 'c', 'd'], sample) + >>> prj == {'a': 1, 'c': 3} + True - Keys should only appear if they were specified and exist in the space. + Keys should only appear if they were specified and exist in the space. - >>> sorted(list(prj.keys())) - ['a', 'c'] + >>> sorted(list(prj.keys())) + ['a', 'c'] - Use the projection to update another dict. + Attempting to access a key not in the projection + results in a KeyError. - >>> target = {'a': 2, 'b': 2} - >>> target.update(prj) - >>> target == {'a': 1, 'b': 2, 'c': 3} - True + >>> prj['b'] + Traceback (most recent call last): + ... + KeyError: 'b' - Also note that Projection keeps a reference to the original dict, so - if you modify the original dict, that could modify the Projection. + Use the projection to update another dict. - >>> del sample['a'] - >>> dict(prj) - {'c': 3} - """ - def __init__(self, keys, space): - self._keys = tuple(keys) - self._space = space + >>> target = {'a': 2, 'b': 2} + >>> target.update(prj) + >>> target == {'a': 1, 'b': 2, 'c': 3} + True - def __getitem__(self, key): - if key not in self._keys: - raise KeyError(key) - return self._space[key] + Also note that Projection keeps a reference to the original dict, so + if you modify the original dict, that could modify the Projection. - def __iter__(self): - return iter(set(self._keys).intersection(self._space)) + >>> del sample['a'] + >>> dict(prj) + {'c': 3} + """ - def __len__(self): - return len(tuple(iter(self))) + def __init__(self, keys, space): + self._keys = tuple(keys) + self._space = space + + def __getitem__(self, key): + if key not in self._keys: + raise KeyError(key) + return self._space[key] + + def __iter__(self): + return iter(set(self._keys).intersection(self._space)) + + def __len__(self): + return len(tuple(iter(self))) -class DictFilter(object): - """ - Takes a dict, and simulates a sub-dict based on the keys. +class DictFilter(collections.abc.Mapping): + """ + Takes a dict, and simulates a sub-dict based on the keys. - >>> sample = {'a': 1, 'b': 2, 'c': 3} - >>> filtered = DictFilter(sample, ['a', 'c']) - >>> filtered == {'a': 1, 'c': 3} - True + >>> sample = {'a': 1, 'b': 2, 'c': 3} + >>> filtered = DictFilter(sample, ['a', 'c']) + >>> filtered == {'a': 1, 'c': 3} + True + >>> set(filtered.values()) == {1, 3} + True + >>> set(filtered.items()) == {('a', 1), ('c', 3)} + True - One can also filter by a regular expression pattern + One can also filter by a regular expression pattern - >>> sample['d'] = 4 - >>> sample['ef'] = 5 + >>> sample['d'] = 4 + >>> sample['ef'] = 5 - Here we filter for only single-character keys + Here we filter for only single-character keys - >>> filtered = DictFilter(sample, include_pattern='.$') - >>> filtered == {'a': 1, 'b': 2, 'c': 3, 'd': 4} - True + >>> filtered = DictFilter(sample, include_pattern='.$') + >>> filtered == {'a': 1, 'b': 2, 'c': 3, 'd': 4} + True - Also note that DictFilter keeps a reference to the original dict, so - if you modify the original dict, that could modify the filtered dict. + >>> filtered['e'] + Traceback (most recent call last): + ... + KeyError: 'e' - >>> del sample['d'] - >>> del sample['a'] - >>> filtered == {'b': 2, 'c': 3} - True + >>> 'e' in filtered + False - """ - def __init__(self, dict, include_keys=[], include_pattern=None): - self.dict = dict - self.specified_keys = set(include_keys) - if include_pattern is not None: - self.include_pattern = re.compile(include_pattern) - else: - # for performance, replace the pattern_keys property - self.pattern_keys = set() + Pattern is useful for excluding keys with a prefix. - def get_pattern_keys(self): - keys = filter(self.include_pattern.match, self.dict.keys()) - return set(keys) - pattern_keys = NonDataProperty(get_pattern_keys) + >>> filtered = DictFilter(sample, include_pattern=r'(?![ace])') + >>> dict(filtered) + {'b': 2, 'd': 4} - @property - def include_keys(self): - return self.specified_keys.union(self.pattern_keys) + Also note that DictFilter keeps a reference to the original dict, so + if you modify the original dict, that could modify the filtered dict. - def keys(self): - return self.include_keys.intersection(self.dict.keys()) + >>> del sample['d'] + >>> dict(filtered) + {'b': 2} + """ - def values(self): - keys = self.keys() - values = map(self.dict.get, keys) - return values + def __init__(self, dict, include_keys=[], include_pattern=None): + self.dict = dict + self.specified_keys = set(include_keys) + if include_pattern is not None: + self.include_pattern = re.compile(include_pattern) + else: + # for performance, replace the pattern_keys property + self.pattern_keys = set() - def __getitem__(self, i): - if i not in self.include_keys: - return KeyError, i - return self.dict[i] + def get_pattern_keys(self): + keys = filter(self.include_pattern.match, self.dict.keys()) + return set(keys) - def items(self): - keys = self.keys() - values = map(self.dict.get, keys) - return zip(keys, values) + pattern_keys = NonDataProperty(get_pattern_keys) - def __eq__(self, other): - return dict(self) == other + @property + def include_keys(self): + return self.specified_keys | self.pattern_keys - def __ne__(self, other): - return dict(self) != other + def __getitem__(self, i): + if i not in self.include_keys: + raise KeyError(i) + return self.dict[i] + + def __iter__(self): + return filter(self.include_keys.__contains__, self.dict.keys()) + + def __len__(self): + return len(list(self)) def dict_map(function, dictionary): - """ - dict_map is much like the built-in function map. It takes a dictionary - and applys a function to the values of that dictionary, returning a - new dictionary with the mapped values in the original keys. + """ + dict_map is much like the built-in function map. It takes a dictionary + and applys a function to the values of that dictionary, returning a + new dictionary with the mapped values in the original keys. - >>> d = dict_map(lambda x:x+1, dict(a=1, b=2)) - >>> d == dict(a=2,b=3) - True - """ - return dict((key, function(value)) for key, value in dictionary.items()) + >>> d = dict_map(lambda x:x+1, dict(a=1, b=2)) + >>> d == dict(a=2,b=3) + True + """ + return dict((key, function(value)) for key, value in dictionary.items()) class RangeMap(dict): - """ - A dictionary-like object that uses the keys as bounds for a range. - Inclusion of the value for that range is determined by the - key_match_comparator, which defaults to less-than-or-equal. - A value is returned for a key if it is the first key that matches in - the sorted list of keys. + """ + A dictionary-like object that uses the keys as bounds for a range. + Inclusion of the value for that range is determined by the + key_match_comparator, which defaults to less-than-or-equal. + A value is returned for a key if it is the first key that matches in + the sorted list of keys. - One may supply keyword parameters to be passed to the sort function used - to sort keys (i.e. cmp [python 2 only], keys, reverse) as sort_params. + One may supply keyword parameters to be passed to the sort function used + to sort keys (i.e. key, reverse) as sort_params. - Let's create a map that maps 1-3 -> 'a', 4-6 -> 'b' + Let's create a map that maps 1-3 -> 'a', 4-6 -> 'b' - >>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy - >>> r[1], r[2], r[3], r[4], r[5], r[6] - ('a', 'a', 'a', 'b', 'b', 'b') + >>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') - Even float values should work so long as the comparison operator - supports it. + Even float values should work so long as the comparison operator + supports it. - >>> r[4.5] - 'b' + >>> r[4.5] + 'b' - But you'll notice that the way rangemap is defined, it must be open-ended - on one side. + But you'll notice that the way rangemap is defined, it must be open-ended + on one side. - >>> r[0] - 'a' - >>> r[-1] - 'a' + >>> r[0] + 'a' + >>> r[-1] + 'a' - One can close the open-end of the RangeMap by using undefined_value + One can close the open-end of the RangeMap by using undefined_value - >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'}) - >>> r[0] - Traceback (most recent call last): - ... - KeyError: 0 + >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'}) + >>> r[0] + Traceback (most recent call last): + ... + KeyError: 0 - One can get the first or last elements in the range by using RangeMap.Item + One can get the first or last elements in the range by using RangeMap.Item - >>> last_item = RangeMap.Item(-1) - >>> r[last_item] - 'b' + >>> last_item = RangeMap.Item(-1) + >>> r[last_item] + 'b' - .last_item is a shortcut for Item(-1) + .last_item is a shortcut for Item(-1) - >>> r[RangeMap.last_item] - 'b' + >>> r[RangeMap.last_item] + 'b' - Sometimes it's useful to find the bounds for a RangeMap + Sometimes it's useful to find the bounds for a RangeMap - >>> r.bounds() - (0, 6) + >>> r.bounds() + (0, 6) - RangeMap supports .get(key, default) + RangeMap supports .get(key, default) - >>> r.get(0, 'not found') - 'not found' + >>> r.get(0, 'not found') + 'not found' - >>> r.get(7, 'not found') - 'not found' - """ - def __init__(self, source, sort_params={}, key_match_comparator=operator.le): - dict.__init__(self, source) - self.sort_params = sort_params - self.match = key_match_comparator + >>> r.get(7, 'not found') + 'not found' - def __getitem__(self, item): - sorted_keys = sorted(self.keys(), **self.sort_params) - if isinstance(item, RangeMap.Item): - result = self.__getitem__(sorted_keys[item]) - else: - key = self._find_first_match_(sorted_keys, item) - result = dict.__getitem__(self, key) - if result is RangeMap.undefined_value: - raise KeyError(key) - return result + One often wishes to define the ranges by their left-most values, + which requires use of sort params and a key_match_comparator. - def get(self, key, default=None): - """ - Return the value for key if key is in the dictionary, else default. - If default is not given, it defaults to None, so that this method - never raises a KeyError. - """ - try: - return self[key] - except KeyError: - return default + >>> r = RangeMap({1: 'a', 4: 'b'}, + ... sort_params=dict(reverse=True), + ... key_match_comparator=operator.ge) + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') - def _find_first_match_(self, keys, item): - is_match = functools.partial(self.match, item) - matches = list(filter(is_match, keys)) - if matches: - return matches[0] - raise KeyError(item) + That wasn't nearly as easy as before, so an alternate constructor + is provided: - def bounds(self): - sorted_keys = sorted(self.keys(), **self.sort_params) - return ( - sorted_keys[RangeMap.first_item], - sorted_keys[RangeMap.last_item], - ) + >>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value}) + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') - # some special values for the RangeMap - undefined_value = type(str('RangeValueUndefined'), (object,), {})() + """ - class Item(int): - "RangeMap Item" - first_item = Item(0) - last_item = Item(-1) + def __init__(self, source, sort_params={}, key_match_comparator=operator.le): + dict.__init__(self, source) + self.sort_params = sort_params + self.match = key_match_comparator + + @classmethod + def left(cls, source): + return cls( + source, sort_params=dict(reverse=True), key_match_comparator=operator.ge + ) + + def __getitem__(self, item): + sorted_keys = sorted(self.keys(), **self.sort_params) + if isinstance(item, RangeMap.Item): + result = self.__getitem__(sorted_keys[item]) + else: + key = self._find_first_match_(sorted_keys, item) + result = dict.__getitem__(self, key) + if result is RangeMap.undefined_value: + raise KeyError(key) + return result + + def get(self, key, default=None): + """ + Return the value for key if key is in the dictionary, else default. + If default is not given, it defaults to None, so that this method + never raises a KeyError. + """ + try: + return self[key] + except KeyError: + return default + + def _find_first_match_(self, keys, item): + is_match = functools.partial(self.match, item) + matches = list(filter(is_match, keys)) + if matches: + return matches[0] + raise KeyError(item) + + def bounds(self): + sorted_keys = sorted(self.keys(), **self.sort_params) + return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item]) + + # some special values for the RangeMap + undefined_value = type(str('RangeValueUndefined'), (), {})() + + class Item(int): + "RangeMap Item" + + first_item = Item(0) + last_item = Item(-1) def __identity(x): - return x + return x def sorted_items(d, key=__identity, reverse=False): - """ - Return the items of the dictionary sorted by the keys + """ + Return the items of the dictionary sorted by the keys - >>> sample = dict(foo=20, bar=42, baz=10) - >>> tuple(sorted_items(sample)) - (('bar', 42), ('baz', 10), ('foo', 20)) + >>> sample = dict(foo=20, bar=42, baz=10) + >>> tuple(sorted_items(sample)) + (('bar', 42), ('baz', 10), ('foo', 20)) - >>> reverse_string = lambda s: ''.join(reversed(s)) - >>> tuple(sorted_items(sample, key=reverse_string)) - (('foo', 20), ('bar', 42), ('baz', 10)) + >>> reverse_string = lambda s: ''.join(reversed(s)) + >>> tuple(sorted_items(sample, key=reverse_string)) + (('foo', 20), ('bar', 42), ('baz', 10)) - >>> tuple(sorted_items(sample, reverse=True)) - (('foo', 20), ('baz', 10), ('bar', 42)) - """ - # wrap the key func so it operates on the first element of each item - def pairkey_key(item): - return key(item[0]) - return sorted(d.items(), key=pairkey_key, reverse=reverse) + >>> tuple(sorted_items(sample, reverse=True)) + (('foo', 20), ('baz', 10), ('bar', 42)) + """ + # wrap the key func so it operates on the first element of each item + def pairkey_key(item): + return key(item[0]) + + return sorted(d.items(), key=pairkey_key, reverse=reverse) class KeyTransformingDict(dict): - """ - A dict subclass that transforms the keys before they're used. - Subclasses may override the default transform_key to customize behavior. - """ - @staticmethod - def transform_key(key): - return key + """ + A dict subclass that transforms the keys before they're used. + Subclasses may override the default transform_key to customize behavior. + """ - def __init__(self, *args, **kargs): - super(KeyTransformingDict, self).__init__() - # build a dictionary using the default constructs - d = dict(*args, **kargs) - # build this dictionary using transformed keys. - for item in d.items(): - self.__setitem__(*item) + @staticmethod + def transform_key(key): # pragma: nocover + return key - def __setitem__(self, key, val): - key = self.transform_key(key) - super(KeyTransformingDict, self).__setitem__(key, val) + def __init__(self, *args, **kargs): + super(KeyTransformingDict, self).__init__() + # build a dictionary using the default constructs + d = dict(*args, **kargs) + # build this dictionary using transformed keys. + for item in d.items(): + self.__setitem__(*item) - def __getitem__(self, key): - key = self.transform_key(key) - return super(KeyTransformingDict, self).__getitem__(key) + def __setitem__(self, key, val): + key = self.transform_key(key) + super(KeyTransformingDict, self).__setitem__(key, val) - def __contains__(self, key): - key = self.transform_key(key) - return super(KeyTransformingDict, self).__contains__(key) + def __getitem__(self, key): + key = self.transform_key(key) + return super(KeyTransformingDict, self).__getitem__(key) - def __delitem__(self, key): - key = self.transform_key(key) - return super(KeyTransformingDict, self).__delitem__(key) + def __contains__(self, key): + key = self.transform_key(key) + return super(KeyTransformingDict, self).__contains__(key) - def get(self, key, *args, **kwargs): - key = self.transform_key(key) - return super(KeyTransformingDict, self).get(key, *args, **kwargs) + def __delitem__(self, key): + key = self.transform_key(key) + return super(KeyTransformingDict, self).__delitem__(key) - def setdefault(self, key, *args, **kwargs): - key = self.transform_key(key) - return super(KeyTransformingDict, self).setdefault(key, *args, **kwargs) + def get(self, key, *args, **kwargs): + key = self.transform_key(key) + return super(KeyTransformingDict, self).get(key, *args, **kwargs) - def pop(self, key, *args, **kwargs): - key = self.transform_key(key) - return super(KeyTransformingDict, self).pop(key, *args, **kwargs) + def setdefault(self, key, *args, **kwargs): + key = self.transform_key(key) + return super(KeyTransformingDict, self).setdefault(key, *args, **kwargs) - def matching_key_for(self, key): - """ - Given a key, return the actual key stored in self that matches. - Raise KeyError if the key isn't found. - """ - try: - return next(e_key for e_key in self.keys() if e_key == key) - except StopIteration: - raise KeyError(key) + def pop(self, key, *args, **kwargs): + key = self.transform_key(key) + return super(KeyTransformingDict, self).pop(key, *args, **kwargs) + + def matching_key_for(self, key): + """ + Given a key, return the actual key stored in self that matches. + Raise KeyError if the key isn't found. + """ + try: + return next(e_key for e_key in self.keys() if e_key == key) + except StopIteration: + raise KeyError(key) class FoldedCaseKeyedDict(KeyTransformingDict): - """ - A case-insensitive dictionary (keys are compared as insensitive - if they are strings). + """ + A case-insensitive dictionary (keys are compared as insensitive + if they are strings). - >>> d = FoldedCaseKeyedDict() - >>> d['heLlo'] = 'world' - >>> list(d.keys()) == ['heLlo'] - True - >>> list(d.values()) == ['world'] - True - >>> d['hello'] == 'world' - True - >>> 'hello' in d - True - >>> 'HELLO' in d - True - >>> print(repr(FoldedCaseKeyedDict({'heLlo': 'world'})).replace("u'", "'")) - {'heLlo': 'world'} - >>> d = FoldedCaseKeyedDict({'heLlo': 'world'}) - >>> print(d['hello']) - world - >>> print(d['Hello']) - world - >>> list(d.keys()) - ['heLlo'] - >>> d = FoldedCaseKeyedDict({'heLlo': 'world', 'Hello': 'world'}) - >>> list(d.values()) - ['world'] - >>> key, = d.keys() - >>> key in ['heLlo', 'Hello'] - True - >>> del d['HELLO'] - >>> d - {} + >>> d = FoldedCaseKeyedDict() + >>> d['heLlo'] = 'world' + >>> list(d.keys()) == ['heLlo'] + True + >>> list(d.values()) == ['world'] + True + >>> d['hello'] == 'world' + True + >>> 'hello' in d + True + >>> 'HELLO' in d + True + >>> print(repr(FoldedCaseKeyedDict({'heLlo': 'world'}))) + {'heLlo': 'world'} + >>> d = FoldedCaseKeyedDict({'heLlo': 'world'}) + >>> print(d['hello']) + world + >>> print(d['Hello']) + world + >>> list(d.keys()) + ['heLlo'] + >>> d = FoldedCaseKeyedDict({'heLlo': 'world', 'Hello': 'world'}) + >>> list(d.values()) + ['world'] + >>> key, = d.keys() + >>> key in ['heLlo', 'Hello'] + True + >>> del d['HELLO'] + >>> d + {} - get should work + get should work - >>> d['Sumthin'] = 'else' - >>> d.get('SUMTHIN') - 'else' - >>> d.get('OTHER', 'thing') - 'thing' - >>> del d['sumthin'] + >>> d['Sumthin'] = 'else' + >>> d.get('SUMTHIN') + 'else' + >>> d.get('OTHER', 'thing') + 'thing' + >>> del d['sumthin'] - setdefault should also work + setdefault should also work - >>> d['This'] = 'that' - >>> print(d.setdefault('this', 'other')) - that - >>> len(d) - 1 - >>> print(d['this']) - that - >>> print(d.setdefault('That', 'other')) - other - >>> print(d['THAT']) - other + >>> d['This'] = 'that' + >>> print(d.setdefault('this', 'other')) + that + >>> len(d) + 1 + >>> print(d['this']) + that + >>> print(d.setdefault('That', 'other')) + other + >>> print(d['THAT']) + other - Make it pop! + Make it pop! - >>> print(d.pop('THAT')) - other + >>> print(d.pop('THAT')) + other - To retrieve the key in its originally-supplied form, use matching_key_for + To retrieve the key in its originally-supplied form, use matching_key_for - >>> print(d.matching_key_for('this')) - This - """ - @staticmethod - def transform_key(key): - return jaraco.text.FoldedCase(key) + >>> print(d.matching_key_for('this')) + This + + >>> d.matching_key_for('missing') + Traceback (most recent call last): + ... + KeyError: 'missing' + """ + + @staticmethod + def transform_key(key): + return jaraco.text.FoldedCase(key) -class DictAdapter(object): - """ - Provide a getitem interface for attributes of an object. +class DictAdapter: + """ + Provide a getitem interface for attributes of an object. - Let's say you want to get at the string.lowercase property in a formatted - string. It's easy with DictAdapter. + Let's say you want to get at the string.lowercase property in a formatted + string. It's easy with DictAdapter. - >>> import string - >>> print("lowercase is %(ascii_lowercase)s" % DictAdapter(string)) - lowercase is abcdefghijklmnopqrstuvwxyz - """ - def __init__(self, wrapped_ob): - self.object = wrapped_ob + >>> import string + >>> print("lowercase is %(ascii_lowercase)s" % DictAdapter(string)) + lowercase is abcdefghijklmnopqrstuvwxyz + """ - def __getitem__(self, name): - return getattr(self.object, name) + def __init__(self, wrapped_ob): + self.object = wrapped_ob + + def __getitem__(self, name): + return getattr(self.object, name) -class ItemsAsAttributes(object): - """ - Mix-in class to enable a mapping object to provide items as - attributes. +class ItemsAsAttributes: + """ + Mix-in class to enable a mapping object to provide items as + attributes. - >>> C = type(str('C'), (dict, ItemsAsAttributes), dict()) - >>> i = C() - >>> i['foo'] = 'bar' - >>> i.foo - 'bar' + >>> C = type(str('C'), (dict, ItemsAsAttributes), dict()) + >>> i = C() + >>> i['foo'] = 'bar' + >>> i.foo + 'bar' - Natural attribute access takes precedence + Natural attribute access takes precedence - >>> i.foo = 'henry' - >>> i.foo - 'henry' + >>> i.foo = 'henry' + >>> i.foo + 'henry' - But as you might expect, the mapping functionality is preserved. + But as you might expect, the mapping functionality is preserved. - >>> i['foo'] - 'bar' + >>> i['foo'] + 'bar' - A normal attribute error should be raised if an attribute is - requested that doesn't exist. + A normal attribute error should be raised if an attribute is + requested that doesn't exist. - >>> i.missing - Traceback (most recent call last): - ... - AttributeError: 'C' object has no attribute 'missing' + >>> i.missing + Traceback (most recent call last): + ... + AttributeError: 'C' object has no attribute 'missing' - It also works on dicts that customize __getitem__ + It also works on dicts that customize __getitem__ - >>> missing_func = lambda self, key: 'missing item' - >>> C = type( - ... str('C'), - ... (dict, ItemsAsAttributes), - ... dict(__missing__ = missing_func), - ... ) - >>> i = C() - >>> i.missing - 'missing item' - >>> i.foo - 'missing item' - """ - def __getattr__(self, key): - try: - return getattr(super(ItemsAsAttributes, self), key) - except AttributeError as e: - # attempt to get the value from the mapping (return self[key]) - # but be careful not to lose the original exception context. - noval = object() + >>> missing_func = lambda self, key: 'missing item' + >>> C = type( + ... str('C'), + ... (dict, ItemsAsAttributes), + ... dict(__missing__ = missing_func), + ... ) + >>> i = C() + >>> i.missing + 'missing item' + >>> i.foo + 'missing item' + """ - def _safe_getitem(cont, key, missing_result): - try: - return cont[key] - except KeyError: - return missing_result - result = _safe_getitem(self, key, noval) - if result is not noval: - return result - # raise the original exception, but use the original class - # name, not 'super'. - message, = e.args - message = message.replace('super', self.__class__.__name__, 1) - e.args = message, - raise + def __getattr__(self, key): + try: + return getattr(super(ItemsAsAttributes, self), key) + except AttributeError as e: + # attempt to get the value from the mapping (return self[key]) + # but be careful not to lose the original exception context. + noval = object() + + def _safe_getitem(cont, key, missing_result): + try: + return cont[key] + except KeyError: + return missing_result + + result = _safe_getitem(self, key, noval) + if result is not noval: + return result + # raise the original exception, but use the original class + # name, not 'super'. + (message,) = e.args + message = message.replace('super', self.__class__.__name__, 1) + e.args = (message,) + raise def invert_map(map): - """ - Given a dictionary, return another dictionary with keys and values - switched. If any of the values resolve to the same key, raises - a ValueError. + """ + Given a dictionary, return another dictionary with keys and values + switched. If any of the values resolve to the same key, raises + a ValueError. - >>> numbers = dict(a=1, b=2, c=3) - >>> letters = invert_map(numbers) - >>> letters[1] - 'a' - >>> numbers['d'] = 3 - >>> invert_map(numbers) - Traceback (most recent call last): - ... - ValueError: Key conflict in inverted mapping - """ - res = dict((v, k) for k, v in map.items()) - if not len(res) == len(map): - raise ValueError('Key conflict in inverted mapping') - return res + >>> numbers = dict(a=1, b=2, c=3) + >>> letters = invert_map(numbers) + >>> letters[1] + 'a' + >>> numbers['d'] = 3 + >>> invert_map(numbers) + Traceback (most recent call last): + ... + ValueError: Key conflict in inverted mapping + """ + res = dict((v, k) for k, v in map.items()) + if not len(res) == len(map): + raise ValueError('Key conflict in inverted mapping') + return res class IdentityOverrideMap(dict): - """ - A dictionary that by default maps each key to itself, but otherwise - acts like a normal dictionary. + """ + A dictionary that by default maps each key to itself, but otherwise + acts like a normal dictionary. - >>> d = IdentityOverrideMap() - >>> d[42] - 42 - >>> d['speed'] = 'speedo' - >>> print(d['speed']) - speedo - """ + >>> d = IdentityOverrideMap() + >>> d[42] + 42 + >>> d['speed'] = 'speedo' + >>> print(d['speed']) + speedo + """ - def __missing__(self, key): - return key + def __missing__(self, key): + return key -class DictStack(list, collections.abc.Mapping): - """ - A stack of dictionaries that behaves as a view on those dictionaries, - giving preference to the last. +class DictStack(list, collections.abc.MutableMapping): + """ + A stack of dictionaries that behaves as a view on those dictionaries, + giving preference to the last. - >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)]) - >>> stack['a'] - 2 - >>> stack['b'] - 2 - >>> stack['c'] - 2 - >>> stack.push(dict(a=3)) - >>> stack['a'] - 3 - >>> set(stack.keys()) == set(['a', 'b', 'c']) - True - >>> d = stack.pop() - >>> stack['a'] - 2 - >>> d = stack.pop() - >>> stack['a'] - 1 - >>> stack.get('b', None) - """ + >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)]) + >>> stack['a'] + 2 + >>> stack['b'] + 2 + >>> stack['c'] + 2 + >>> len(stack) + 3 + >>> stack.push(dict(a=3)) + >>> stack['a'] + 3 + >>> stack['a'] = 4 + >>> set(stack.keys()) == set(['a', 'b', 'c']) + True + >>> set(stack.items()) == set([('a', 4), ('b', 2), ('c', 2)]) + True + >>> dict(**stack) == dict(stack) == dict(a=4, c=2, b=2) + True + >>> d = stack.pop() + >>> stack['a'] + 2 + >>> d = stack.pop() + >>> stack['a'] + 1 + >>> stack.get('b', None) + >>> 'c' in stack + True + >>> del stack['c'] + >>> dict(stack) + {'a': 1} + """ - def keys(self): - return list(set(itertools.chain.from_iterable(c.keys() for c in self))) + def __iter__(self): + dicts = list.__iter__(self) + return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts))) - def __getitem__(self, key): - for scope in reversed(self): - if key in scope: - return scope[key] - raise KeyError(key) + def __getitem__(self, key): + for scope in reversed(tuple(list.__iter__(self))): + if key in scope: + return scope[key] + raise KeyError(key) - push = list.append + push = list.append + + def __contains__(self, other): + return collections.abc.Mapping.__contains__(self, other) + + def __len__(self): + return len(list(iter(self))) + + def __setitem__(self, key, item): + last = list.__getitem__(self, -1) + return last.__setitem__(key, item) + + def __delitem__(self, key): + last = list.__getitem__(self, -1) + return last.__delitem__(key) + + # workaround for mypy confusion + def pop(self, *args, **kwargs): + return list.pop(self, *args, **kwargs) class BijectiveMap(dict): - """ - A Bijective Map (two-way mapping). + """ + A Bijective Map (two-way mapping). - Implemented as a simple dictionary of 2x the size, mapping values back - to keys. + Implemented as a simple dictionary of 2x the size, mapping values back + to keys. - Note, this implementation may be incomplete. If there's not a test for - your use case below, it's likely to fail, so please test and send pull - requests or patches for additional functionality needed. + Note, this implementation may be incomplete. If there's not a test for + your use case below, it's likely to fail, so please test and send pull + requests or patches for additional functionality needed. - >>> m = BijectiveMap() - >>> m['a'] = 'b' - >>> m == {'a': 'b', 'b': 'a'} - True - >>> print(m['b']) - a + >>> m = BijectiveMap() + >>> m['a'] = 'b' + >>> m == {'a': 'b', 'b': 'a'} + True + >>> print(m['b']) + a - >>> m['c'] = 'd' - >>> len(m) - 2 + >>> m['c'] = 'd' + >>> len(m) + 2 - Some weird things happen if you map an item to itself or overwrite a - single key of a pair, so it's disallowed. + Some weird things happen if you map an item to itself or overwrite a + single key of a pair, so it's disallowed. - >>> m['e'] = 'e' - Traceback (most recent call last): - ValueError: Key cannot map to itself + >>> m['e'] = 'e' + Traceback (most recent call last): + ValueError: Key cannot map to itself - >>> m['d'] = 'e' - Traceback (most recent call last): - ValueError: Key/Value pairs may not overlap + >>> m['d'] = 'e' + Traceback (most recent call last): + ValueError: Key/Value pairs may not overlap - >>> m['e'] = 'd' - Traceback (most recent call last): - ValueError: Key/Value pairs may not overlap + >>> m['e'] = 'd' + Traceback (most recent call last): + ValueError: Key/Value pairs may not overlap - >>> print(m.pop('d')) - c + >>> print(m.pop('d')) + c - >>> 'c' in m - False + >>> 'c' in m + False - >>> m = BijectiveMap(dict(a='b')) - >>> len(m) - 1 - >>> print(m['b']) - a + >>> m = BijectiveMap(dict(a='b')) + >>> len(m) + 1 + >>> print(m['b']) + a - >>> m = BijectiveMap() - >>> m.update(a='b') - >>> m['b'] - 'a' + >>> m = BijectiveMap() + >>> m.update(a='b') + >>> m['b'] + 'a' - >>> del m['b'] - >>> len(m) - 0 - >>> 'a' in m - False - """ - def __init__(self, *args, **kwargs): - super(BijectiveMap, self).__init__() - self.update(*args, **kwargs) + >>> del m['b'] + >>> len(m) + 0 + >>> 'a' in m + False + """ - def __setitem__(self, item, value): - if item == value: - raise ValueError("Key cannot map to itself") - overlap = ( - item in self and self[item] != value - or - value in self and self[value] != item - ) - if overlap: - raise ValueError("Key/Value pairs may not overlap") - super(BijectiveMap, self).__setitem__(item, value) - super(BijectiveMap, self).__setitem__(value, item) + def __init__(self, *args, **kwargs): + super(BijectiveMap, self).__init__() + self.update(*args, **kwargs) - def __delitem__(self, item): - self.pop(item) + def __setitem__(self, item, value): + if item == value: + raise ValueError("Key cannot map to itself") + overlap = ( + item in self + and self[item] != value + or value in self + and self[value] != item + ) + if overlap: + raise ValueError("Key/Value pairs may not overlap") + super(BijectiveMap, self).__setitem__(item, value) + super(BijectiveMap, self).__setitem__(value, item) - def __len__(self): - return super(BijectiveMap, self).__len__() // 2 + def __delitem__(self, item): + self.pop(item) - def pop(self, key, *args, **kwargs): - mirror = self[key] - super(BijectiveMap, self).__delitem__(mirror) - return super(BijectiveMap, self).pop(key, *args, **kwargs) + def __len__(self): + return super(BijectiveMap, self).__len__() // 2 - def update(self, *args, **kwargs): - # build a dictionary using the default constructs - d = dict(*args, **kwargs) - # build this dictionary using transformed keys. - for item in d.items(): - self.__setitem__(*item) + def pop(self, key, *args, **kwargs): + mirror = self[key] + super(BijectiveMap, self).__delitem__(mirror) + return super(BijectiveMap, self).pop(key, *args, **kwargs) + + def update(self, *args, **kwargs): + # build a dictionary using the default constructs + d = dict(*args, **kwargs) + # build this dictionary using transformed keys. + for item in d.items(): + self.__setitem__(*item) class FrozenDict(collections.abc.Mapping, collections.abc.Hashable): - """ - An immutable mapping. + """ + An immutable mapping. - >>> a = FrozenDict(a=1, b=2) - >>> b = FrozenDict(a=1, b=2) - >>> a == b - True + >>> a = FrozenDict(a=1, b=2) + >>> b = FrozenDict(a=1, b=2) + >>> a == b + True - >>> a == dict(a=1, b=2) - True - >>> dict(a=1, b=2) == a - True + >>> a == dict(a=1, b=2) + True + >>> dict(a=1, b=2) == a + True + >>> 'a' in a + True + >>> type(hash(a)) is type(0) + True + >>> set(iter(a)) == {'a', 'b'} + True + >>> len(a) + 2 + >>> a['a'] == a.get('a') == 1 + True - >>> a['c'] = 3 - Traceback (most recent call last): - ... - TypeError: 'FrozenDict' object does not support item assignment + >>> a['c'] = 3 + Traceback (most recent call last): + ... + TypeError: 'FrozenDict' object does not support item assignment - >>> a.update(y=3) - Traceback (most recent call last): - ... - AttributeError: 'FrozenDict' object has no attribute 'update' + >>> a.update(y=3) + Traceback (most recent call last): + ... + AttributeError: 'FrozenDict' object has no attribute 'update' - Copies should compare equal + Copies should compare equal - >>> copy.copy(a) == a - True + >>> copy.copy(a) == a + True - Copies should be the same type + Copies should be the same type - >>> isinstance(copy.copy(a), FrozenDict) - True + >>> isinstance(copy.copy(a), FrozenDict) + True - FrozenDict supplies .copy(), even though - collections.abc.Mapping doesn't demand it. + FrozenDict supplies .copy(), even though + collections.abc.Mapping doesn't demand it. - >>> a.copy() == a - True - >>> a.copy() is not a - True - """ - __slots__ = ['__data'] + >>> a.copy() == a + True + >>> a.copy() is not a + True + """ - def __new__(cls, *args, **kwargs): - self = super(FrozenDict, cls).__new__(cls) - self.__data = dict(*args, **kwargs) - return self + __slots__ = ['__data'] - # Container - def __contains__(self, key): - return key in self.__data + def __new__(cls, *args, **kwargs): + self = super(FrozenDict, cls).__new__(cls) + self.__data = dict(*args, **kwargs) + return self - # Hashable - def __hash__(self): - return hash(tuple(sorted(self.__data.iteritems()))) + # Container + def __contains__(self, key): + return key in self.__data - # Mapping - def __iter__(self): - return iter(self.__data) + # Hashable + def __hash__(self): + return hash(tuple(sorted(self.__data.items()))) - def __len__(self): - return len(self.__data) + # Mapping + def __iter__(self): + return iter(self.__data) - def __getitem__(self, key): - return self.__data[key] + def __len__(self): + return len(self.__data) - # override get for efficiency provided by dict - def get(self, *args, **kwargs): - return self.__data.get(*args, **kwargs) + def __getitem__(self, key): + return self.__data[key] - # override eq to recognize underlying implementation - def __eq__(self, other): - if isinstance(other, FrozenDict): - other = other.__data - return self.__data.__eq__(other) + # override get for efficiency provided by dict + def get(self, *args, **kwargs): + return self.__data.get(*args, **kwargs) - def copy(self): - "Return a shallow copy of self" - return copy.copy(self) + # override eq to recognize underlying implementation + def __eq__(self, other): + if isinstance(other, FrozenDict): + other = other.__data + return self.__data.__eq__(other) + + def copy(self): + "Return a shallow copy of self" + return copy.copy(self) class Enumeration(ItemsAsAttributes, BijectiveMap): - """ - A convenient way to provide enumerated values + """ + A convenient way to provide enumerated values - >>> e = Enumeration('a b c') - >>> e['a'] - 0 + >>> e = Enumeration('a b c') + >>> e['a'] + 0 - >>> e.a - 0 + >>> e.a + 0 - >>> e[1] - 'b' + >>> e[1] + 'b' - >>> set(e.names) == set('abc') - True + >>> set(e.names) == set('abc') + True - >>> set(e.codes) == set(range(3)) - True + >>> set(e.codes) == set(range(3)) + True - >>> e.get('d') is None - True + >>> e.get('d') is None + True - Codes need not start with 0 + Codes need not start with 0 - >>> e = Enumeration('a b c', range(1, 4)) - >>> e['a'] - 1 + >>> e = Enumeration('a b c', range(1, 4)) + >>> e['a'] + 1 - >>> e[3] - 'c' - """ - def __init__(self, names, codes=None): - if isinstance(names, six.string_types): - names = names.split() - if codes is None: - codes = itertools.count() - super(Enumeration, self).__init__(zip(names, codes)) + >>> e[3] + 'c' + """ - @property - def names(self): - return (key for key in self if isinstance(key, six.string_types)) + def __init__(self, names, codes=None): + if isinstance(names, str): + names = names.split() + if codes is None: + codes = itertools.count() + super(Enumeration, self).__init__(zip(names, codes)) - @property - def codes(self): - return (self[name] for name in self.names) + @property + def names(self): + return (key for key in self if isinstance(key, str)) + + @property + def codes(self): + return (self[name] for name in self.names) -class Everything(object): - """ - A collection "containing" every possible thing. +class Everything: + """ + A collection "containing" every possible thing. - >>> 'foo' in Everything() - True + >>> 'foo' in Everything() + True - >>> import random - >>> random.randint(1, 999) in Everything() - True + >>> import random + >>> random.randint(1, 999) in Everything() + True - >>> random.choice([None, 'foo', 42, ('a', 'b', 'c')]) in Everything() - True - """ - def __contains__(self, other): - return True + >>> random.choice([None, 'foo', 42, ('a', 'b', 'c')]) in Everything() + True + """ + + def __contains__(self, other): + return True -class InstrumentedDict(six.moves.UserDict): - """ - Instrument an existing dictionary with additional - functionality, but always reference and mutate - the original dictionary. +class InstrumentedDict(collections.UserDict): # type: ignore # buggy mypy + """ + Instrument an existing dictionary with additional + functionality, but always reference and mutate + the original dictionary. - >>> orig = {'a': 1, 'b': 2} - >>> inst = InstrumentedDict(orig) - >>> inst['a'] - 1 - >>> inst['c'] = 3 - >>> orig['c'] - 3 - >>> inst.keys() == orig.keys() - True - """ - def __init__(self, data): - six.moves.UserDict.__init__(self) - self.data = data + >>> orig = {'a': 1, 'b': 2} + >>> inst = InstrumentedDict(orig) + >>> inst['a'] + 1 + >>> inst['c'] = 3 + >>> orig['c'] + 3 + >>> inst.keys() == orig.keys() + True + """ + + def __init__(self, data): + super().__init__() + self.data = data -class Least(object): - """ - A value that is always lesser than any other +class Least: + """ + A value that is always lesser than any other - >>> least = Least() - >>> 3 < least - False - >>> 3 > least - True - >>> least < 3 - True - >>> least <= 3 - True - >>> least > 3 - False - >>> 'x' > least - True - >>> None > least - True - """ + >>> least = Least() + >>> 3 < least + False + >>> 3 > least + True + >>> least < 3 + True + >>> least <= 3 + True + >>> least > 3 + False + >>> 'x' > least + True + >>> None > least + True + """ - def __le__(self, other): - return True - __lt__ = __le__ + def __le__(self, other): + return True - def __ge__(self, other): - return False - __gt__ = __ge__ + __lt__ = __le__ + + def __ge__(self, other): + return False + + __gt__ = __ge__ -class Greatest(object): - """ - A value that is always greater than any other +class Greatest: + """ + A value that is always greater than any other - >>> greatest = Greatest() - >>> 3 < greatest - True - >>> 3 > greatest - False - >>> greatest < 3 - False - >>> greatest > 3 - True - >>> greatest >= 3 - True - >>> 'x' > greatest - False - >>> None > greatest - False - """ + >>> greatest = Greatest() + >>> 3 < greatest + True + >>> 3 > greatest + False + >>> greatest < 3 + False + >>> greatest > 3 + True + >>> greatest >= 3 + True + >>> 'x' > greatest + False + >>> None > greatest + False + """ - def __ge__(self, other): - return True - __gt__ = __ge__ + def __ge__(self, other): + return True - def __le__(self, other): - return False - __lt__ = __le__ + __gt__ = __ge__ + + def __le__(self, other): + return False + + __lt__ = __le__ + + +def pop_all(items): + """ + Clear items in place and return a copy of items. + + >>> items = [1, 2, 3] + >>> popped = pop_all(items) + >>> popped is items + False + >>> popped + [1, 2, 3] + >>> items + [] + """ + result, items[:] = items[:], [] + return result + + +# mypy disabled for pytest-dev/pytest#8332 +class FreezableDefaultDict(collections.defaultdict): # type: ignore + """ + Often it is desirable to prevent the mutation of + a default dict after its initial construction, such + as to prevent mutation during iteration. + + >>> dd = FreezableDefaultDict(list) + >>> dd[0].append('1') + >>> dd.freeze() + >>> dd[1] + [] + >>> len(dd) + 1 + """ + + def __missing__(self, key): + return getattr(self, '_frozen', super().__missing__)(key) + + def freeze(self): + self._frozen = lambda key: self.default_factory() + + +class Accumulator: + def __init__(self, initial=0): + self.val = initial + + def __call__(self, val): + self.val += val + return self.val + + +class WeightedLookup(RangeMap): + """ + Given parameters suitable for a dict representing keys + and a weighted proportion, return a RangeMap representing + spans of values proportial to the weights: + + >>> even = WeightedLookup(a=1, b=1) + + [0, 1) -> a + [1, 2) -> b + + >>> lk = WeightedLookup(a=1, b=2) + + [0, 1) -> a + [1, 3) -> b + + >>> lk[.5] + 'a' + >>> lk[1.5] + 'b' + + Adds ``.random()`` to select a random weighted value: + + >>> lk.random() in ['a', 'b'] + True + + >>> choices = [lk.random() for x in range(1000)] + + Statistically speaking, choices should be .5 a:b + >>> ratio = choices.count('a') / choices.count('b') + >>> .4 < ratio < .6 + True + """ + + def __init__(self, *args, **kwargs): + raw = dict(*args, **kwargs) + + # allocate keys by weight + indexes = map(Accumulator(), raw.values()) + super().__init__(zip(indexes, raw.keys()), key_match_comparator=operator.lt) + + def random(self): + lower, upper = self.bounds() + selector = random.random() * upper + return self[selector] diff --git a/libs/win/jaraco/context.py b/libs/win/jaraco/context.py new file mode 100644 index 00000000..818f16f3 --- /dev/null +++ b/libs/win/jaraco/context.py @@ -0,0 +1,253 @@ +import os +import subprocess +import contextlib +import functools +import tempfile +import shutil +import operator + + +@contextlib.contextmanager +def pushd(dir): + orig = os.getcwd() + os.chdir(dir) + try: + yield dir + finally: + os.chdir(orig) + + +@contextlib.contextmanager +def tarball_context(url, target_dir=None, runner=None, pushd=pushd): + """ + Get a tarball, extract it, change to that directory, yield, then + clean up. + `runner` is the function to invoke commands. + `pushd` is a context manager for changing the directory. + """ + if target_dir is None: + target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '') + if runner is None: + runner = functools.partial(subprocess.check_call, shell=True) + # In the tar command, use --strip-components=1 to strip the first path and + # then + # use -C to cause the files to be extracted to {target_dir}. This ensures + # that we always know where the files were extracted. + runner('mkdir {target_dir}'.format(**vars())) + try: + getter = 'wget {url} -O -' + extract = 'tar x{compression} --strip-components=1 -C {target_dir}' + cmd = ' | '.join((getter, extract)) + runner(cmd.format(compression=infer_compression(url), **vars())) + with pushd(target_dir): + yield target_dir + finally: + runner('rm -Rf {target_dir}'.format(**vars())) + + +def infer_compression(url): + """ + Given a URL or filename, infer the compression code for tar. + """ + # cheat and just assume it's the last two characters + compression_indicator = url[-2:] + mapping = dict(gz='z', bz='j', xz='J') + # Assume 'z' (gzip) if no match + return mapping.get(compression_indicator, 'z') + + +@contextlib.contextmanager +def temp_dir(remover=shutil.rmtree): + """ + Create a temporary directory context. Pass a custom remover + to override the removal behavior. + """ + temp_dir = tempfile.mkdtemp() + try: + yield temp_dir + finally: + remover(temp_dir) + + +@contextlib.contextmanager +def repo_context(url, branch=None, quiet=True, dest_ctx=temp_dir): + """ + Check out the repo indicated by url. + + If dest_ctx is supplied, it should be a context manager + to yield the target directory for the check out. + """ + exe = 'git' if 'git' in url else 'hg' + with dest_ctx() as repo_dir: + cmd = [exe, 'clone', url, repo_dir] + if branch: + cmd.extend(['--branch', branch]) + devnull = open(os.path.devnull, 'w') + stdout = devnull if quiet else None + subprocess.check_call(cmd, stdout=stdout) + yield repo_dir + + +@contextlib.contextmanager +def null(): + yield + + +class ExceptionTrap: + """ + A context manager that will catch certain exceptions and provide an + indication they occurred. + + >>> with ExceptionTrap() as trap: + ... raise Exception() + >>> bool(trap) + True + + >>> with ExceptionTrap() as trap: + ... pass + >>> bool(trap) + False + + >>> with ExceptionTrap(ValueError) as trap: + ... raise ValueError("1 + 1 is not 3") + >>> bool(trap) + True + + >>> with ExceptionTrap(ValueError) as trap: + ... raise Exception() + Traceback (most recent call last): + ... + Exception + + >>> bool(trap) + False + """ + + exc_info = None, None, None + + def __init__(self, exceptions=(Exception,)): + self.exceptions = exceptions + + def __enter__(self): + return self + + @property + def type(self): + return self.exc_info[0] + + @property + def value(self): + return self.exc_info[1] + + @property + def tb(self): + return self.exc_info[2] + + def __exit__(self, *exc_info): + type = exc_info[0] + matches = type and issubclass(type, self.exceptions) + if matches: + self.exc_info = exc_info + return matches + + def __bool__(self): + return bool(self.type) + + def raises(self, func, *, _test=bool): + """ + Wrap func and replace the result with the truth + value of the trap (True if an exception occurred). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> raises = ExceptionTrap(ValueError).raises + + Now decorate a function that always fails. + + >>> @raises + ... def fail(): + ... raise ValueError('failed') + >>> fail() + True + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + with ExceptionTrap(self.exceptions) as trap: + func(*args, **kwargs) + return _test(trap) + + return wrapper + + def passes(self, func): + """ + Wrap func and replace the result with the truth + value of the trap (True if no exception). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> passes = ExceptionTrap(ValueError).passes + + Now decorate a function that always fails. + + >>> @passes + ... def fail(): + ... raise ValueError('failed') + + >>> fail() + False + """ + return self.raises(func, _test=operator.not_) + + +class suppress(contextlib.suppress, contextlib.ContextDecorator): + """ + A version of contextlib.suppress with decorator support. + + >>> @suppress(KeyError) + ... def key_error(): + ... {}[''] + >>> key_error() + """ + + +class on_interrupt(contextlib.ContextDecorator): + """ + Replace a KeyboardInterrupt with SystemExit(1) + + >>> def do_interrupt(): + ... raise KeyboardInterrupt() + >>> on_interrupt('error')(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 1 + >>> on_interrupt('error', code=255)(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 255 + >>> on_interrupt('suppress')(do_interrupt)() + >>> with __import__('pytest').raises(KeyboardInterrupt): + ... on_interrupt('ignore')(do_interrupt)() + """ + + def __init__( + self, + action='error', + # py3.7 compat + # /, + code=1, + ): + self.action = action + self.code = code + + def __enter__(self): + return self + + def __exit__(self, exctype, excinst, exctb): + if exctype is not KeyboardInterrupt or self.action == 'ignore': + return + elif self.action == 'error': + raise SystemExit(self.code) from excinst + return self.action == 'suppress' diff --git a/libs/win/jaraco/functools.py b/libs/win/jaraco/functools.py index 134102a7..fcdbb4f9 100644 --- a/libs/win/jaraco/functools.py +++ b/libs/win/jaraco/functools.py @@ -1,459 +1,525 @@ -from __future__ import ( - absolute_import, unicode_literals, print_function, division, -) - import functools import time -import warnings import inspect import collections -from itertools import count +import types +import itertools -__metaclass__ = type +import more_itertools + +from typing import Callable, TypeVar -try: - from functools import lru_cache -except ImportError: - try: - from backports.functools_lru_cache import lru_cache - except ImportError: - try: - from functools32 import lru_cache - except ImportError: - warnings.warn("No lru_cache available") - - -import more_itertools.recipes +CallableT = TypeVar("CallableT", bound=Callable[..., object]) def compose(*funcs): - """ - Compose any number of unary functions into a single unary function. + """ + Compose any number of unary functions into a single unary function. - >>> import textwrap - >>> from six import text_type - >>> stripped = text_type.strip(textwrap.dedent(compose.__doc__)) - >>> compose(text_type.strip, textwrap.dedent)(compose.__doc__) == stripped - True + >>> import textwrap + >>> expected = str.strip(textwrap.dedent(compose.__doc__)) + >>> strip_and_dedent = compose(str.strip, textwrap.dedent) + >>> strip_and_dedent(compose.__doc__) == expected + True - Compose also allows the innermost function to take arbitrary arguments. + Compose also allows the innermost function to take arbitrary arguments. - >>> round_three = lambda x: round(x, ndigits=3) - >>> f = compose(round_three, int.__truediv__) - >>> [f(3*x, x+1) for x in range(1,10)] - [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7] - """ + >>> round_three = lambda x: round(x, ndigits=3) + >>> f = compose(round_three, int.__truediv__) + >>> [f(3*x, x+1) for x in range(1,10)] + [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7] + """ - def compose_two(f1, f2): - return lambda *args, **kwargs: f1(f2(*args, **kwargs)) - return functools.reduce(compose_two, funcs) + def compose_two(f1, f2): + return lambda *args, **kwargs: f1(f2(*args, **kwargs)) + + return functools.reduce(compose_two, funcs) def method_caller(method_name, *args, **kwargs): - """ - Return a function that will call a named method on the - target object with optional positional and keyword - arguments. + """ + Return a function that will call a named method on the + target object with optional positional and keyword + arguments. - >>> lower = method_caller('lower') - >>> lower('MyString') - 'mystring' - """ - def call_method(target): - func = getattr(target, method_name) - return func(*args, **kwargs) - return call_method + >>> lower = method_caller('lower') + >>> lower('MyString') + 'mystring' + """ + + def call_method(target): + func = getattr(target, method_name) + return func(*args, **kwargs) + + return call_method def once(func): - """ - Decorate func so it's only ever called the first time. + """ + Decorate func so it's only ever called the first time. - This decorator can ensure that an expensive or non-idempotent function - will not be expensive on subsequent calls and is idempotent. + This decorator can ensure that an expensive or non-idempotent function + will not be expensive on subsequent calls and is idempotent. - >>> add_three = once(lambda a: a+3) - >>> add_three(3) - 6 - >>> add_three(9) - 6 - >>> add_three('12') - 6 + >>> add_three = once(lambda a: a+3) + >>> add_three(3) + 6 + >>> add_three(9) + 6 + >>> add_three('12') + 6 - To reset the stored value, simply clear the property ``saved_result``. + To reset the stored value, simply clear the property ``saved_result``. - >>> del add_three.saved_result - >>> add_three(9) - 12 - >>> add_three(8) - 12 + >>> del add_three.saved_result + >>> add_three(9) + 12 + >>> add_three(8) + 12 - Or invoke 'reset()' on it. + Or invoke 'reset()' on it. - >>> add_three.reset() - >>> add_three(-3) - 0 - >>> add_three(0) - 0 - """ - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not hasattr(wrapper, 'saved_result'): - wrapper.saved_result = func(*args, **kwargs) - return wrapper.saved_result - wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result') - return wrapper + >>> add_three.reset() + >>> add_three(-3) + 0 + >>> add_three(0) + 0 + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(wrapper, 'saved_result'): + wrapper.saved_result = func(*args, **kwargs) + return wrapper.saved_result + + wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result') + return wrapper -def method_cache(method, cache_wrapper=None): - """ - Wrap lru_cache to support storing the cache data in the object instances. +def method_cache( + method: CallableT, + cache_wrapper: Callable[ + [CallableT], CallableT + ] = functools.lru_cache(), # type: ignore[assignment] +) -> CallableT: + """ + Wrap lru_cache to support storing the cache data in the object instances. - Abstracts the common paradigm where the method explicitly saves an - underscore-prefixed protected property on first call and returns that - subsequently. + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. - >>> class MyClass: - ... calls = 0 - ... - ... @method_cache - ... def method(self, value): - ... self.calls += 1 - ... return value + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value - >>> a = MyClass() - >>> a.method(3) - 3 - >>> for x in range(75): - ... res = a.method(x) - >>> a.calls - 75 + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 - Note that the apparent behavior will be exactly like that of lru_cache - except that the cache is stored on each instance, so values in one - instance will not flush values from another, and when an instance is - deleted, so are the cached values for that instance. + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. - >>> b = MyClass() - >>> for x in range(35): - ... res = b.method(x) - >>> b.calls - 35 - >>> a.method(0) - 0 - >>> a.calls - 75 + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 - Note that if method had been decorated with ``functools.lru_cache()``, - a.calls would have been 76 (due to the cached value of 0 having been - flushed by the 'b' instance). + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). - Clear the cache with ``.cache_clear()`` + Clear the cache with ``.cache_clear()`` - >>> a.method.cache_clear() + >>> a.method.cache_clear() - Another cache wrapper may be supplied: + Same for a method that hasn't yet been called. - >>> cache = lru_cache(maxsize=2) - >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) - >>> a = MyClass() - >>> a.method2() - 3 + >>> c = MyClass() + >>> c.method.cache_clear() - Caution - do not subsequently wrap the method with another decorator, such - as ``@property``, which changes the semantics of the function. + Another cache wrapper may be supplied: - See also - http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ - for another implementation and additional justification. - """ - cache_wrapper = cache_wrapper or lru_cache() + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 - def wrapper(self, *args, **kwargs): - # it's the first call, replace the method with a cached, bound method - bound_method = functools.partial(method, self) - cached_method = cache_wrapper(bound_method) - setattr(self, method.__name__, cached_method) - return cached_method(*args, **kwargs) + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. - return _special_method_cache(method, cache_wrapper) or wrapper + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + + def wrapper(self: object, *args: object, **kwargs: object) -> object: + # it's the first call, replace the method with a cached, bound method + bound_method: CallableT = types.MethodType( # type: ignore[assignment] + method, self + ) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None # type: ignore[attr-defined] + + return ( # type: ignore[return-value] + _special_method_cache(method, cache_wrapper) or wrapper + ) def _special_method_cache(method, cache_wrapper): - """ - Because Python treats special methods differently, it's not - possible to use instance attributes to implement the cached - methods. + """ + Because Python treats special methods differently, it's not + possible to use instance attributes to implement the cached + methods. - Instead, install the wrapper method under a different name - and return a simple proxy to that wrapper. + Instead, install the wrapper method under a different name + and return a simple proxy to that wrapper. - https://github.com/jaraco/jaraco.functools/issues/5 - """ - name = method.__name__ - special_names = '__getattr__', '__getitem__' - if name not in special_names: - return + https://github.com/jaraco/jaraco.functools/issues/5 + """ + name = method.__name__ + special_names = '__getattr__', '__getitem__' + if name not in special_names: + return - wrapper_name = '__cached' + name + wrapper_name = '__cached' + name - def proxy(self, *args, **kwargs): - if wrapper_name not in vars(self): - bound = functools.partial(method, self) - cache = cache_wrapper(bound) - setattr(self, wrapper_name, cache) - else: - cache = getattr(self, wrapper_name) - return cache(*args, **kwargs) + def proxy(self, *args, **kwargs): + if wrapper_name not in vars(self): + bound = types.MethodType(method, self) + cache = cache_wrapper(bound) + setattr(self, wrapper_name, cache) + else: + cache = getattr(self, wrapper_name) + return cache(*args, **kwargs) - return proxy + return proxy def apply(transform): - """ - Decorate a function with a transform function that is - invoked on results returned from the decorated function. + """ + Decorate a function with a transform function that is + invoked on results returned from the decorated function. - >>> @apply(reversed) - ... def get_numbers(start): - ... return range(start, start+3) - >>> list(get_numbers(4)) - [6, 5, 4] - """ - def wrap(func): - return compose(transform, func) - return wrap + >>> @apply(reversed) + ... def get_numbers(start): + ... "doc for get_numbers" + ... return range(start, start+3) + >>> list(get_numbers(4)) + [6, 5, 4] + >>> get_numbers.__doc__ + 'doc for get_numbers' + """ + + def wrap(func): + return functools.wraps(func)(compose(transform, func)) + + return wrap def result_invoke(action): - r""" - Decorate a function with an action function that is - invoked on the results returned from the decorated - function (for its side-effect), then return the original - result. + r""" + Decorate a function with an action function that is + invoked on the results returned from the decorated + function (for its side-effect), then return the original + result. - >>> @result_invoke(print) - ... def add_two(a, b): - ... return a + b - >>> x = add_two(2, 3) - 5 - """ - def wrap(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - action(result) - return result - return wrapper - return wrap + >>> @result_invoke(print) + ... def add_two(a, b): + ... return a + b + >>> x = add_two(2, 3) + 5 + >>> x + 5 + """ + + def wrap(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + action(result) + return result + + return wrapper + + return wrap def call_aside(f, *args, **kwargs): - """ - Call a function for its side effect after initialization. + """ + Call a function for its side effect after initialization. - >>> @call_aside - ... def func(): print("called") - called - >>> func() - called + >>> @call_aside + ... def func(): print("called") + called + >>> func() + called - Use functools.partial to pass parameters to the initial call + Use functools.partial to pass parameters to the initial call - >>> @functools.partial(call_aside, name='bingo') - ... def func(name): print("called with", name) - called with bingo - """ - f(*args, **kwargs) - return f + >>> @functools.partial(call_aside, name='bingo') + ... def func(name): print("called with", name) + called with bingo + """ + f(*args, **kwargs) + return f class Throttler: - """ - Rate-limit a function (or other callable) - """ - def __init__(self, func, max_rate=float('Inf')): - if isinstance(func, Throttler): - func = func.func - self.func = func - self.max_rate = max_rate - self.reset() + """ + Rate-limit a function (or other callable) + """ - def reset(self): - self.last_called = 0 + def __init__(self, func, max_rate=float('Inf')): + if isinstance(func, Throttler): + func = func.func + self.func = func + self.max_rate = max_rate + self.reset() - def __call__(self, *args, **kwargs): - self._wait() - return self.func(*args, **kwargs) + def reset(self): + self.last_called = 0 - def _wait(self): - "ensure at least 1/max_rate seconds from last call" - elapsed = time.time() - self.last_called - must_wait = 1 / self.max_rate - elapsed - time.sleep(max(0, must_wait)) - self.last_called = time.time() + def __call__(self, *args, **kwargs): + self._wait() + return self.func(*args, **kwargs) - def __get__(self, obj, type=None): - return first_invoke(self._wait, functools.partial(self.func, obj)) + def _wait(self): + "ensure at least 1/max_rate seconds from last call" + elapsed = time.time() - self.last_called + must_wait = 1 / self.max_rate - elapsed + time.sleep(max(0, must_wait)) + self.last_called = time.time() + + def __get__(self, obj, type=None): + return first_invoke(self._wait, functools.partial(self.func, obj)) def first_invoke(func1, func2): - """ - Return a function that when invoked will invoke func1 without - any parameters (for its side-effect) and then invoke func2 - with whatever parameters were passed, returning its result. - """ - def wrapper(*args, **kwargs): - func1() - return func2(*args, **kwargs) - return wrapper + """ + Return a function that when invoked will invoke func1 without + any parameters (for its side-effect) and then invoke func2 + with whatever parameters were passed, returning its result. + """ + + def wrapper(*args, **kwargs): + func1() + return func2(*args, **kwargs) + + return wrapper def retry_call(func, cleanup=lambda: None, retries=0, trap=()): - """ - Given a callable func, trap the indicated exceptions - for up to 'retries' times, invoking cleanup on the - exception. On the final attempt, allow any exceptions - to propagate. - """ - attempts = count() if retries == float('inf') else range(retries) - for attempt in attempts: - try: - return func() - except trap: - cleanup() + """ + Given a callable func, trap the indicated exceptions + for up to 'retries' times, invoking cleanup on the + exception. On the final attempt, allow any exceptions + to propagate. + """ + attempts = itertools.count() if retries == float('inf') else range(retries) + for attempt in attempts: + try: + return func() + except trap: + cleanup() - return func() + return func() def retry(*r_args, **r_kwargs): - """ - Decorator wrapper for retry_call. Accepts arguments to retry_call - except func and then returns a decorator for the decorated function. + """ + Decorator wrapper for retry_call. Accepts arguments to retry_call + except func and then returns a decorator for the decorated function. - Ex: + Ex: - >>> @retry(retries=3) - ... def my_func(a, b): - ... "this is my funk" - ... print(a, b) - >>> my_func.__doc__ - 'this is my funk' - """ - def decorate(func): - @functools.wraps(func) - def wrapper(*f_args, **f_kwargs): - bound = functools.partial(func, *f_args, **f_kwargs) - return retry_call(bound, *r_args, **r_kwargs) - return wrapper - return decorate + >>> @retry(retries=3) + ... def my_func(a, b): + ... "this is my funk" + ... print(a, b) + >>> my_func.__doc__ + 'this is my funk' + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*f_args, **f_kwargs): + bound = functools.partial(func, *f_args, **f_kwargs) + return retry_call(bound, *r_args, **r_kwargs) + + return wrapper + + return decorate def print_yielded(func): - """ - Convert a generator into a function that prints all yielded elements + """ + Convert a generator into a function that prints all yielded elements - >>> @print_yielded - ... def x(): - ... yield 3; yield None - >>> x() - 3 - None - """ - print_all = functools.partial(map, print) - print_results = compose(more_itertools.recipes.consume, print_all, func) - return functools.wraps(func)(print_results) + >>> @print_yielded + ... def x(): + ... yield 3; yield None + >>> x() + 3 + None + """ + print_all = functools.partial(map, print) + print_results = compose(more_itertools.consume, print_all, func) + return functools.wraps(func)(print_results) def pass_none(func): - """ - Wrap func so it's not called if its first param is None + """ + Wrap func so it's not called if its first param is None - >>> print_text = pass_none(print) - >>> print_text('text') - text - >>> print_text(None) - """ - @functools.wraps(func) - def wrapper(param, *args, **kwargs): - if param is not None: - return func(param, *args, **kwargs) - return wrapper + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + + return wrapper def assign_params(func, namespace): - """ - Assign parameters from namespace where func solicits. + """ + Assign parameters from namespace where func solicits. - >>> def func(x, y=3): - ... print(x, y) - >>> assigned = assign_params(func, dict(x=2, z=4)) - >>> assigned() - 2 3 + >>> def func(x, y=3): + ... print(x, y) + >>> assigned = assign_params(func, dict(x=2, z=4)) + >>> assigned() + 2 3 - The usual errors are raised if a function doesn't receive - its required parameters: + The usual errors are raised if a function doesn't receive + its required parameters: - >>> assigned = assign_params(func, dict(y=3, z=4)) - >>> assigned() - Traceback (most recent call last): - TypeError: func() ...argument... - """ - try: - sig = inspect.signature(func) - params = sig.parameters.keys() - except AttributeError: - spec = inspect.getargspec(func) - params = spec.args - call_ns = { - k: namespace[k] - for k in params - if k in namespace - } - return functools.partial(func, **call_ns) + >>> assigned = assign_params(func, dict(y=3, z=4)) + >>> assigned() + Traceback (most recent call last): + TypeError: func() ...argument... + + It even works on methods: + + >>> class Handler: + ... def meth(self, arg): + ... print(arg) + >>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))() + crystal + """ + sig = inspect.signature(func) + params = sig.parameters.keys() + call_ns = {k: namespace[k] for k in params if k in namespace} + return functools.partial(func, **call_ns) def save_method_args(method): - """ - Wrap a method such that when it is called, the args and kwargs are - saved on the method. + """ + Wrap a method such that when it is called, the args and kwargs are + saved on the method. - >>> class MyClass: - ... @save_method_args - ... def method(self, a, b): - ... print(a, b) - >>> my_ob = MyClass() - >>> my_ob.method(1, 2) - 1 2 - >>> my_ob._saved_method.args - (1, 2) - >>> my_ob._saved_method.kwargs - {} - >>> my_ob.method(a=3, b='foo') - 3 foo - >>> my_ob._saved_method.args - () - >>> my_ob._saved_method.kwargs == dict(a=3, b='foo') - True + >>> class MyClass: + ... @save_method_args + ... def method(self, a, b): + ... print(a, b) + >>> my_ob = MyClass() + >>> my_ob.method(1, 2) + 1 2 + >>> my_ob._saved_method.args + (1, 2) + >>> my_ob._saved_method.kwargs + {} + >>> my_ob.method(a=3, b='foo') + 3 foo + >>> my_ob._saved_method.args + () + >>> my_ob._saved_method.kwargs == dict(a=3, b='foo') + True - The arguments are stored on the instance, allowing for - different instance to save different args. + The arguments are stored on the instance, allowing for + different instance to save different args. - >>> your_ob = MyClass() - >>> your_ob.method({str('x'): 3}, b=[4]) - {'x': 3} [4] - >>> your_ob._saved_method.args - ({'x': 3},) - >>> my_ob._saved_method.args - () - """ - args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') + >>> your_ob = MyClass() + >>> your_ob.method({str('x'): 3}, b=[4]) + {'x': 3} [4] + >>> your_ob._saved_method.args + ({'x': 3},) + >>> my_ob._saved_method.args + () + """ + args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') - @functools.wraps(method) - def wrapper(self, *args, **kwargs): - attr_name = '_saved_' + method.__name__ - attr = args_and_kwargs(args, kwargs) - setattr(self, attr_name, attr) - return method(self, *args, **kwargs) - return wrapper + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + attr_name = '_saved_' + method.__name__ + attr = args_and_kwargs(args, kwargs) + setattr(self, attr_name, attr) + return method(self, *args, **kwargs) + + return wrapper + + +def except_(*exceptions, replace=None, use=None): + """ + Replace the indicated exceptions, if raised, with the indicated + literal replacement or evaluated expression (if present). + + >>> safe_int = except_(ValueError)(int) + >>> safe_int('five') + >>> safe_int('5') + 5 + + Specify a literal replacement with ``replace``. + + >>> safe_int_r = except_(ValueError, replace=0)(int) + >>> safe_int_r('five') + 0 + + Provide an expression to ``use`` to pass through particular parameters. + + >>> safe_int_pt = except_(ValueError, use='args[0]')(int) + >>> safe_int_pt('five') + 'five' + + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except exceptions: + try: + return eval(use) + except TypeError: + return replace + + return wrapper + + return decorate diff --git a/libs/win/jaraco/structures/binary.py b/libs/win/jaraco/structures/binary.py index be57cc76..c4cbbeda 100644 --- a/libs/win/jaraco/structures/binary.py +++ b/libs/win/jaraco/structures/binary.py @@ -1,151 +1,156 @@ -from __future__ import absolute_import, unicode_literals - import numbers from functools import reduce def get_bit_values(number, size=32): - """ - Get bit values as a list for a given number + """ + Get bit values as a list for a given number - >>> get_bit_values(1) == [0]*31 + [1] - True + >>> get_bit_values(1) == [0]*31 + [1] + True - >>> get_bit_values(0xDEADBEEF) - [1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1] + >>> get_bit_values(0xDEADBEEF) + [1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, \ +1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1] - You may override the default word size of 32-bits to match your actual - application. + You may override the default word size of 32-bits to match your actual + application. - >>> get_bit_values(0x3, 2) - [1, 1] + >>> get_bit_values(0x3, 2) + [1, 1] - >>> get_bit_values(0x3, 4) - [0, 0, 1, 1] - """ - number += 2**size - return list(map(int, bin(number)[-size:])) + >>> get_bit_values(0x3, 4) + [0, 0, 1, 1] + """ + number += 2 ** size + return list(map(int, bin(number)[-size:])) def gen_bit_values(number): - """ - Return a zero or one for each bit of a numeric value up to the most - significant 1 bit, beginning with the least significant bit. + """ + Return a zero or one for each bit of a numeric value up to the most + significant 1 bit, beginning with the least significant bit. - >>> list(gen_bit_values(16)) - [0, 0, 0, 0, 1] - """ - digits = bin(number)[2:] - return map(int, reversed(digits)) + >>> list(gen_bit_values(16)) + [0, 0, 0, 0, 1] + """ + digits = bin(number)[2:] + return map(int, reversed(digits)) def coalesce(bits): - """ - Take a sequence of bits, most significant first, and - coalesce them into a number. + """ + Take a sequence of bits, most significant first, and + coalesce them into a number. - >>> coalesce([1,0,1]) - 5 - """ - operation = lambda a, b: (a << 1 | b) - return reduce(operation, bits) + >>> coalesce([1,0,1]) + 5 + """ + + def operation(a, b): + return a << 1 | b + + return reduce(operation, bits) -class Flags(object): - """ - Subclasses should define _names, a list of flag names beginning - with the least-significant bit. +class Flags: + """ + Subclasses should define _names, a list of flag names beginning + with the least-significant bit. - >>> class MyFlags(Flags): - ... _names = 'a', 'b', 'c' - >>> mf = MyFlags.from_number(5) - >>> mf['a'] - 1 - >>> mf['b'] - 0 - >>> mf['c'] == mf[2] - True - >>> mf['b'] = 1 - >>> mf['a'] = 0 - >>> mf.number - 6 - """ - def __init__(self, values): - self._values = list(values) - if hasattr(self, '_names'): - n_missing_bits = len(self._names) - len(self._values) - self._values.extend([0] * n_missing_bits) + >>> class MyFlags(Flags): + ... _names = 'a', 'b', 'c' + >>> mf = MyFlags.from_number(5) + >>> mf['a'] + 1 + >>> mf['b'] + 0 + >>> mf['c'] == mf[2] + True + >>> mf['b'] = 1 + >>> mf['a'] = 0 + >>> mf.number + 6 + """ - @classmethod - def from_number(cls, number): - return cls(gen_bit_values(number)) + def __init__(self, values): + self._values = list(values) + if hasattr(self, '_names'): + n_missing_bits = len(self._names) - len(self._values) + self._values.extend([0] * n_missing_bits) - @property - def number(self): - return coalesce(reversed(self._values)) + @classmethod + def from_number(cls, number): + return cls(gen_bit_values(number)) - def __setitem__(self, key, value): - # first try by index, then by name - try: - self._values[key] = value - except TypeError: - index = self._names.index(key) - self._values[index] = value + @property + def number(self): + return coalesce(reversed(self._values)) - def __getitem__(self, key): - # first try by index, then by name - try: - return self._values[key] - except TypeError: - index = self._names.index(key) - return self._values[index] + def __setitem__(self, key, value): + # first try by index, then by name + try: + self._values[key] = value + except TypeError: + index = self._names.index(key) + self._values[index] = value + + def __getitem__(self, key): + # first try by index, then by name + try: + return self._values[key] + except TypeError: + index = self._names.index(key) + return self._values[index] class BitMask(type): - """ - A metaclass to create a bitmask with attributes. Subclass an int and - set this as the metaclass to use. + """ + A metaclass to create a bitmask with attributes. Subclass an int and + set this as the metaclass to use. - Here's how to create such a class on Python 3: + Construct such a class: - class MyBits(int, metaclass=BitMask): - a = 0x1 - b = 0x4 - c = 0x3 + >>> class MyBits(int, metaclass=BitMask): + ... a = 0x1 + ... b = 0x4 + ... c = 0x3 - For testing purposes, construct explicitly to support Python 2 + >>> b1 = MyBits(3) + >>> b1.a, b1.b, b1.c + (True, False, True) + >>> b2 = MyBits(8) + >>> any([b2.a, b2.b, b2.c]) + False - >>> ns = dict(a=0x1, b=0x4, c=0x3) - >>> MyBits = BitMask(str('MyBits'), (int,), ns) + If the instance defines methods, they won't be wrapped in + properties. - >>> b1 = MyBits(3) - >>> b1.a, b1.b, b1.c - (True, False, True) - >>> b2 = MyBits(8) - >>> any([b2.a, b2.b, b2.c]) - False + >>> class MyBits(int, metaclass=BitMask): + ... a = 0x1 + ... b = 0x4 + ... c = 0x3 + ... + ... @classmethod + ... def get_value(cls): + ... return 'some value' + ... + ... @property + ... def prop(cls): + ... return 'a property' + >>> MyBits(3).get_value() + 'some value' + >>> MyBits(3).prop + 'a property' + """ - If the instance defines methods, they won't be wrapped in - properties. + def __new__(cls, name, bases, attrs): + def make_property(name, value): + if name.startswith('_') or not isinstance(value, numbers.Number): + return value + return property(lambda self, value=value: bool(self & value)) - >>> ns['get_value'] = classmethod(lambda cls: 'some value') - >>> ns['prop'] = property(lambda self: 'a property') - >>> MyBits = BitMask(str('MyBits'), (int,), ns) - - >>> MyBits(3).get_value() - 'some value' - >>> MyBits(3).prop - 'a property' - """ - - def __new__(cls, name, bases, attrs): - def make_property(name, value): - if name.startswith('_') or not isinstance(value, numbers.Number): - return value - return property(lambda self, value=value: bool(self & value)) - - newattrs = dict( - (name, make_property(name, value)) - for name, value in attrs.items() - ) - return type.__new__(cls, name, bases, newattrs) + newattrs = dict( + (name, make_property(name, value)) for name, value in attrs.items() + ) + return type.__new__(cls, name, bases, newattrs) diff --git a/libs/win/jaraco/text.py b/libs/win/jaraco/text.py deleted file mode 100644 index 71b4b0bc..00000000 --- a/libs/win/jaraco/text.py +++ /dev/null @@ -1,452 +0,0 @@ -from __future__ import absolute_import, unicode_literals, print_function - -import sys -import re -import inspect -import itertools -import textwrap -import functools - -import six - -import jaraco.collections -from jaraco.functools import compose - - -def substitution(old, new): - """ - Return a function that will perform a substitution on a string - """ - return lambda s: s.replace(old, new) - - -def multi_substitution(*substitutions): - """ - Take a sequence of pairs specifying substitutions, and create - a function that performs those substitutions. - - >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo') - 'baz' - """ - substitutions = itertools.starmap(substitution, substitutions) - # compose function applies last function first, so reverse the - # substitutions to get the expected order. - substitutions = reversed(tuple(substitutions)) - return compose(*substitutions) - - -class FoldedCase(six.text_type): - """ - A case insensitive string class; behaves just like str - except compares equal when the only variation is case. - - >>> s = FoldedCase('hello world') - - >>> s == 'Hello World' - True - - >>> 'Hello World' == s - True - - >>> s != 'Hello World' - False - - >>> s.index('O') - 4 - - >>> s.split('O') - ['hell', ' w', 'rld'] - - >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) - ['alpha', 'Beta', 'GAMMA'] - - Sequence membership is straightforward. - - >>> "Hello World" in [s] - True - >>> s in ["Hello World"] - True - - You may test for set inclusion, but candidate and elements - must both be folded. - - >>> FoldedCase("Hello World") in {s} - True - >>> s in {FoldedCase("Hello World")} - True - - String inclusion works as long as the FoldedCase object - is on the right. - - >>> "hello" in FoldedCase("Hello World") - True - - But not if the FoldedCase object is on the left: - - >>> FoldedCase('hello') in 'Hello World' - False - - In that case, use in_: - - >>> FoldedCase('hello').in_('Hello World') - True - - """ - def __lt__(self, other): - return self.lower() < other.lower() - - def __gt__(self, other): - return self.lower() > other.lower() - - def __eq__(self, other): - return self.lower() == other.lower() - - def __ne__(self, other): - return self.lower() != other.lower() - - def __hash__(self): - return hash(self.lower()) - - def __contains__(self, other): - return super(FoldedCase, self).lower().__contains__(other.lower()) - - def in_(self, other): - "Does self appear in other?" - return self in FoldedCase(other) - - # cache lower since it's likely to be called frequently. - @jaraco.functools.method_cache - def lower(self): - return super(FoldedCase, self).lower() - - def index(self, sub): - return self.lower().index(sub.lower()) - - def split(self, splitter=' ', maxsplit=0): - pattern = re.compile(re.escape(splitter), re.I) - return pattern.split(self, maxsplit) - - -def local_format(string): - """ - format the string using variables in the caller's local namespace. - - >>> a = 3 - >>> local_format("{a:5}") - ' 3' - """ - context = inspect.currentframe().f_back.f_locals - if sys.version_info < (3, 2): - return string.format(**context) - return string.format_map(context) - - -def global_format(string): - """ - format the string using variables in the caller's global namespace. - - >>> a = 3 - >>> fmt = "The func name: {global_format.__name__}" - >>> global_format(fmt) - 'The func name: global_format' - """ - context = inspect.currentframe().f_back.f_globals - if sys.version_info < (3, 2): - return string.format(**context) - return string.format_map(context) - - -def namespace_format(string): - """ - Format the string using variable in the caller's scope (locals + globals). - - >>> a = 3 - >>> fmt = "A is {a} and this func is {namespace_format.__name__}" - >>> namespace_format(fmt) - 'A is 3 and this func is namespace_format' - """ - context = jaraco.collections.DictStack() - context.push(inspect.currentframe().f_back.f_globals) - context.push(inspect.currentframe().f_back.f_locals) - if sys.version_info < (3, 2): - return string.format(**context) - return string.format_map(context) - - -def is_decodable(value): - r""" - Return True if the supplied value is decodable (using the default - encoding). - - >>> is_decodable(b'\xff') - False - >>> is_decodable(b'\x32') - True - """ - # TODO: This code could be expressed more consisely and directly - # with a jaraco.context.ExceptionTrap, but that adds an unfortunate - # long dependency tree, so for now, use boolean literals. - try: - value.decode() - except UnicodeDecodeError: - return False - return True - - -def is_binary(value): - """ - Return True if the value appears to be binary (that is, it's a byte - string and isn't decodable). - """ - return isinstance(value, bytes) and not is_decodable(value) - - -def trim(s): - r""" - Trim something like a docstring to remove the whitespace that - is common due to indentation and formatting. - - >>> trim("\n\tfoo = bar\n\t\tbar = baz\n") - 'foo = bar\n\tbar = baz' - """ - return textwrap.dedent(s).strip() - - -class Splitter(object): - """object that will split a string with the given arguments for each call - - >>> s = Splitter(',') - >>> s('hello, world, this is your, master calling') - ['hello', ' world', ' this is your', ' master calling'] - """ - def __init__(self, *args): - self.args = args - - def __call__(self, s): - return s.split(*self.args) - - -def indent(string, prefix=' ' * 4): - return prefix + string - - -class WordSet(tuple): - """ - Given a Python identifier, return the words that identifier represents, - whether in camel case, underscore-separated, etc. - - >>> WordSet.parse("camelCase") - ('camel', 'Case') - - >>> WordSet.parse("under_sep") - ('under', 'sep') - - Acronyms should be retained - - >>> WordSet.parse("firstSNL") - ('first', 'SNL') - - >>> WordSet.parse("you_and_I") - ('you', 'and', 'I') - - >>> WordSet.parse("A simple test") - ('A', 'simple', 'test') - - Multiple caps should not interfere with the first cap of another word. - - >>> WordSet.parse("myABCClass") - ('my', 'ABC', 'Class') - - The result is a WordSet, so you can get the form you need. - - >>> WordSet.parse("myABCClass").underscore_separated() - 'my_ABC_Class' - - >>> WordSet.parse('a-command').camel_case() - 'ACommand' - - >>> WordSet.parse('someIdentifier').lowered().space_separated() - 'some identifier' - - Slices of the result should return another WordSet. - - >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated() - 'out_of_context' - - >>> WordSet.from_class_name(WordSet()).lowered().space_separated() - 'word set' - """ - _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))') - - def capitalized(self): - return WordSet(word.capitalize() for word in self) - - def lowered(self): - return WordSet(word.lower() for word in self) - - def camel_case(self): - return ''.join(self.capitalized()) - - def headless_camel_case(self): - words = iter(self) - first = next(words).lower() - return itertools.chain((first,), WordSet(words).camel_case()) - - def underscore_separated(self): - return '_'.join(self) - - def dash_separated(self): - return '-'.join(self) - - def space_separated(self): - return ' '.join(self) - - def __getitem__(self, item): - result = super(WordSet, self).__getitem__(item) - if isinstance(item, slice): - result = WordSet(result) - return result - - # for compatibility with Python 2 - def __getslice__(self, i, j): - return self.__getitem__(slice(i, j)) - - @classmethod - def parse(cls, identifier): - matches = cls._pattern.finditer(identifier) - return WordSet(match.group(0) for match in matches) - - @classmethod - def from_class_name(cls, subject): - return cls.parse(subject.__class__.__name__) - - -# for backward compatibility -words = WordSet.parse - - -def simple_html_strip(s): - r""" - Remove HTML from the string `s`. - - >>> str(simple_html_strip('')) - '' - - >>> print(simple_html_strip('A stormy day in paradise')) - A stormy day in paradise - - >>> print(simple_html_strip('Somebody tell the truth.')) - Somebody tell the truth. - - >>> print(simple_html_strip('What about
\nmultiple lines?')) - What about - multiple lines? - """ - html_stripper = re.compile('()|(<[^>]*>)|([^<]+)', re.DOTALL) - texts = ( - match.group(3) or '' - for match - in html_stripper.finditer(s) - ) - return ''.join(texts) - - -class SeparatedValues(six.text_type): - """ - A string separated by a separator. Overrides __iter__ for getting - the values. - - >>> list(SeparatedValues('a,b,c')) - ['a', 'b', 'c'] - - Whitespace is stripped and empty values are discarded. - - >>> list(SeparatedValues(' a, b , c, ')) - ['a', 'b', 'c'] - """ - separator = ',' - - def __iter__(self): - parts = self.split(self.separator) - return six.moves.filter(None, (part.strip() for part in parts)) - - -class Stripper: - r""" - Given a series of lines, find the common prefix and strip it from them. - - >>> lines = [ - ... 'abcdefg\n', - ... 'abc\n', - ... 'abcde\n', - ... ] - >>> res = Stripper.strip_prefix(lines) - >>> res.prefix - 'abc' - >>> list(res.lines) - ['defg\n', '\n', 'de\n'] - - If no prefix is common, nothing should be stripped. - - >>> lines = [ - ... 'abcd\n', - ... '1234\n', - ... ] - >>> res = Stripper.strip_prefix(lines) - >>> res.prefix = '' - >>> list(res.lines) - ['abcd\n', '1234\n'] - """ - def __init__(self, prefix, lines): - self.prefix = prefix - self.lines = map(self, lines) - - @classmethod - def strip_prefix(cls, lines): - prefix_lines, lines = itertools.tee(lines) - prefix = functools.reduce(cls.common_prefix, prefix_lines) - return cls(prefix, lines) - - def __call__(self, line): - if not self.prefix: - return line - null, prefix, rest = line.partition(self.prefix) - return rest - - @staticmethod - def common_prefix(s1, s2): - """ - Return the common prefix of two lines. - """ - index = min(len(s1), len(s2)) - while s1[:index] != s2[:index]: - index -= 1 - return s1[:index] - - -def remove_prefix(text, prefix): - """ - Remove the prefix from the text if it exists. - - >>> remove_prefix('underwhelming performance', 'underwhelming ') - 'performance' - - >>> remove_prefix('something special', 'sample') - 'something special' - """ - null, prefix, rest = text.rpartition(prefix) - return rest - - -def remove_suffix(text, suffix): - """ - Remove the suffix from the text if it exists. - - >>> remove_suffix('name.git', '.git') - 'name' - - >>> remove_suffix('something special', 'sample') - 'something special' - """ - rest, suffix, null = text.partition(suffix) - return rest diff --git a/libs/win/jaraco/text/Lorem ipsum.txt b/libs/win/jaraco/text/Lorem ipsum.txt new file mode 100644 index 00000000..986f944b --- /dev/null +++ b/libs/win/jaraco/text/Lorem ipsum.txt @@ -0,0 +1,2 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. diff --git a/libs/win/jaraco/text/__init__.py b/libs/win/jaraco/text/__init__.py new file mode 100644 index 00000000..e51101c2 --- /dev/null +++ b/libs/win/jaraco/text/__init__.py @@ -0,0 +1,622 @@ +import re +import itertools +import textwrap +import functools + +try: + from importlib.resources import files # type: ignore +except ImportError: # pragma: nocover + from importlib_resources import files # type: ignore + +from jaraco.functools import compose, method_cache +from jaraco.context import ExceptionTrap + + +def substitution(old, new): + """ + Return a function that will perform a substitution on a string + """ + return lambda s: s.replace(old, new) + + +def multi_substitution(*substitutions): + """ + Take a sequence of pairs specifying substitutions, and create + a function that performs those substitutions. + + >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo') + 'baz' + """ + substitutions = itertools.starmap(substitution, substitutions) + # compose function applies last function first, so reverse the + # substitutions to get the expected order. + substitutions = reversed(tuple(substitutions)) + return compose(*substitutions) + + +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + Allows testing for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use ``in_``: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + + >>> FoldedCase('ß') == FoldedCase('ss') + True + """ + + def __lt__(self, other): + return self.casefold() < other.casefold() + + def __gt__(self, other): + return self.casefold() > other.casefold() + + def __eq__(self, other): + return self.casefold() == other.casefold() + + def __ne__(self, other): + return self.casefold() != other.casefold() + + def __hash__(self): + return hash(self.casefold()) + + def __contains__(self, other): + return super().casefold().__contains__(other.casefold()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache casefold since it's likely to be called frequently. + @method_cache + def casefold(self): + return super().casefold() + + def index(self, sub): + return self.casefold().index(sub.casefold()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) + + +# Python 3.8 compatibility +_unicode_trap = ExceptionTrap(UnicodeDecodeError) + + +@_unicode_trap.passes +def is_decodable(value): + r""" + Return True if the supplied value is decodable (using the default + encoding). + + >>> is_decodable(b'\xff') + False + >>> is_decodable(b'\x32') + True + """ + value.decode() + + +def is_binary(value): + r""" + Return True if the value appears to be binary (that is, it's a byte + string and isn't decodable). + + >>> is_binary(b'\xff') + True + >>> is_binary('\xff') + False + """ + return isinstance(value, bytes) and not is_decodable(value) + + +def trim(s): + r""" + Trim something like a docstring to remove the whitespace that + is common due to indentation and formatting. + + >>> trim("\n\tfoo = bar\n\t\tbar = baz\n") + 'foo = bar\n\tbar = baz' + """ + return textwrap.dedent(s).strip() + + +def wrap(s): + """ + Wrap lines of text, retaining existing newlines as + paragraph markers. + + >>> print(wrap(lorem_ipsum)) + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam + varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus + magna felis sollicitudin mauris. Integer in mauris eu nibh euismod + gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis + risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, + eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas + fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla + a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, + neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing + sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque + nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus + quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, + molestie eu, feugiat in, orci. In hac habitasse platea dictumst. + """ + paragraphs = s.splitlines() + wrapped = ('\n'.join(textwrap.wrap(para)) for para in paragraphs) + return '\n\n'.join(wrapped) + + +def unwrap(s): + r""" + Given a multi-line string, return an unwrapped version. + + >>> wrapped = wrap(lorem_ipsum) + >>> wrapped.count('\n') + 20 + >>> unwrapped = unwrap(wrapped) + >>> unwrapped.count('\n') + 1 + >>> print(unwrapped) + Lorem ipsum dolor sit amet, consectetur adipiscing ... + Curabitur pretium tincidunt lacus. Nulla gravida orci ... + + """ + paragraphs = re.split(r'\n\n+', s) + cleaned = (para.replace('\n', ' ') for para in paragraphs) + return '\n'.join(cleaned) + + +lorem_ipsum: str = files(__name__).joinpath('Lorem ipsum.txt').read_text() + + +class Splitter(object): + """object that will split a string with the given arguments for each call + + >>> s = Splitter(',') + >>> s('hello, world, this is your, master calling') + ['hello', ' world', ' this is your', ' master calling'] + """ + + def __init__(self, *args): + self.args = args + + def __call__(self, s): + return s.split(*self.args) + + +def indent(string, prefix=' ' * 4): + """ + >>> indent('foo') + ' foo' + """ + return prefix + string + + +class WordSet(tuple): + """ + Given an identifier, return the words that identifier represents, + whether in camel case, underscore-separated, etc. + + >>> WordSet.parse("camelCase") + ('camel', 'Case') + + >>> WordSet.parse("under_sep") + ('under', 'sep') + + Acronyms should be retained + + >>> WordSet.parse("firstSNL") + ('first', 'SNL') + + >>> WordSet.parse("you_and_I") + ('you', 'and', 'I') + + >>> WordSet.parse("A simple test") + ('A', 'simple', 'test') + + Multiple caps should not interfere with the first cap of another word. + + >>> WordSet.parse("myABCClass") + ('my', 'ABC', 'Class') + + The result is a WordSet, providing access to other forms. + + >>> WordSet.parse("myABCClass").underscore_separated() + 'my_ABC_Class' + + >>> WordSet.parse('a-command').camel_case() + 'ACommand' + + >>> WordSet.parse('someIdentifier').lowered().space_separated() + 'some identifier' + + Slices of the result should return another WordSet. + + >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated() + 'out_of_context' + + >>> WordSet.from_class_name(WordSet()).lowered().space_separated() + 'word set' + + >>> example = WordSet.parse('figured it out') + >>> example.headless_camel_case() + 'figuredItOut' + >>> example.dash_separated() + 'figured-it-out' + + """ + + _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))') + + def capitalized(self): + return WordSet(word.capitalize() for word in self) + + def lowered(self): + return WordSet(word.lower() for word in self) + + def camel_case(self): + return ''.join(self.capitalized()) + + def headless_camel_case(self): + words = iter(self) + first = next(words).lower() + new_words = itertools.chain((first,), WordSet(words).camel_case()) + return ''.join(new_words) + + def underscore_separated(self): + return '_'.join(self) + + def dash_separated(self): + return '-'.join(self) + + def space_separated(self): + return ' '.join(self) + + def trim_right(self, item): + """ + Remove the item from the end of the set. + + >>> WordSet.parse('foo bar').trim_right('foo') + ('foo', 'bar') + >>> WordSet.parse('foo bar').trim_right('bar') + ('foo',) + >>> WordSet.parse('').trim_right('bar') + () + """ + return self[:-1] if self and self[-1] == item else self + + def trim_left(self, item): + """ + Remove the item from the beginning of the set. + + >>> WordSet.parse('foo bar').trim_left('foo') + ('bar',) + >>> WordSet.parse('foo bar').trim_left('bar') + ('foo', 'bar') + >>> WordSet.parse('').trim_left('bar') + () + """ + return self[1:] if self and self[0] == item else self + + def trim(self, item): + """ + >>> WordSet.parse('foo bar').trim('foo') + ('bar',) + """ + return self.trim_left(item).trim_right(item) + + def __getitem__(self, item): + result = super(WordSet, self).__getitem__(item) + if isinstance(item, slice): + result = WordSet(result) + return result + + @classmethod + def parse(cls, identifier): + matches = cls._pattern.finditer(identifier) + return WordSet(match.group(0) for match in matches) + + @classmethod + def from_class_name(cls, subject): + return cls.parse(subject.__class__.__name__) + + +# for backward compatibility +words = WordSet.parse + + +def simple_html_strip(s): + r""" + Remove HTML from the string `s`. + + >>> str(simple_html_strip('')) + '' + + >>> print(simple_html_strip('A stormy day in paradise')) + A stormy day in paradise + + >>> print(simple_html_strip('Somebody tell the truth.')) + Somebody tell the truth. + + >>> print(simple_html_strip('What about
\nmultiple lines?')) + What about + multiple lines? + """ + html_stripper = re.compile('()|(<[^>]*>)|([^<]+)', re.DOTALL) + texts = (match.group(3) or '' for match in html_stripper.finditer(s)) + return ''.join(texts) + + +class SeparatedValues(str): + """ + A string separated by a separator. Overrides __iter__ for getting + the values. + + >>> list(SeparatedValues('a,b,c')) + ['a', 'b', 'c'] + + Whitespace is stripped and empty values are discarded. + + >>> list(SeparatedValues(' a, b , c, ')) + ['a', 'b', 'c'] + """ + + separator = ',' + + def __iter__(self): + parts = self.split(self.separator) + return filter(None, (part.strip() for part in parts)) + + +class Stripper: + r""" + Given a series of lines, find the common prefix and strip it from them. + + >>> lines = [ + ... 'abcdefg\n', + ... 'abc\n', + ... 'abcde\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix + 'abc' + >>> list(res.lines) + ['defg\n', '\n', 'de\n'] + + If no prefix is common, nothing should be stripped. + + >>> lines = [ + ... 'abcd\n', + ... '1234\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix = '' + >>> list(res.lines) + ['abcd\n', '1234\n'] + """ + + def __init__(self, prefix, lines): + self.prefix = prefix + self.lines = map(self, lines) + + @classmethod + def strip_prefix(cls, lines): + prefix_lines, lines = itertools.tee(lines) + prefix = functools.reduce(cls.common_prefix, prefix_lines) + return cls(prefix, lines) + + def __call__(self, line): + if not self.prefix: + return line + null, prefix, rest = line.partition(self.prefix) + return rest + + @staticmethod + def common_prefix(s1, s2): + """ + Return the common prefix of two lines. + """ + index = min(len(s1), len(s2)) + while s1[:index] != s2[:index]: + index -= 1 + return s1[:index] + + +def remove_prefix(text, prefix): + """ + Remove the prefix from the text if it exists. + + >>> remove_prefix('underwhelming performance', 'underwhelming ') + 'performance' + + >>> remove_prefix('something special', 'sample') + 'something special' + """ + null, prefix, rest = text.rpartition(prefix) + return rest + + +def remove_suffix(text, suffix): + """ + Remove the suffix from the text if it exists. + + >>> remove_suffix('name.git', '.git') + 'name' + + >>> remove_suffix('something special', 'sample') + 'something special' + """ + rest, suffix, null = text.partition(suffix) + return rest + + +def normalize_newlines(text): + r""" + Replace alternate newlines with the canonical newline. + + >>> normalize_newlines('Lorem Ipsum\u2029') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\r\n') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\x85') + 'Lorem Ipsum\n' + """ + newlines = ['\r\n', '\r', '\n', '\u0085', '\u2028', '\u2029'] + pattern = '|'.join(newlines) + return re.sub(pattern, '\n', text) + + +def _nonblank(str): + return str and not str.startswith('#') + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) + + +def drop_comment(line): + """ + Drop comments. + + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' + """ + return line.partition(' #')[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. + + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceeding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith('\\'): + try: + item = item[:-2].strip() + next(lines) + except StopIteration: + return + yield item + + +def read_newlines(filename, limit=1024): + r""" + >>> tmp_path = getfixture('tmp_path') + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\n', newline='') + >>> read_newlines(filename) + '\n' + >>> _ = filename.write_text('foo\r\n', newline='') + >>> read_newlines(filename) + '\r\n' + >>> _ = filename.write_text('foo\r\nbar\nbing\r', newline='') + >>> read_newlines(filename) + ('\r', '\n', '\r\n') + """ + with open(filename) as fp: + fp.read(limit) + return fp.newlines diff --git a/libs/win/jaraco/text/layouts.py b/libs/win/jaraco/text/layouts.py new file mode 100644 index 00000000..9636f0f7 --- /dev/null +++ b/libs/win/jaraco/text/layouts.py @@ -0,0 +1,25 @@ +qwerty = "-=qwertyuiop[]asdfghjkl;'zxcvbnm,./_+QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" +dvorak = "[]',.pyfgcrl/=aoeuidhtns-;qjkxbmwvz{}\"<>PYFGCRL?+AOEUIDHTNS_:QJKXBMWVZ" + + +to_dvorak = str.maketrans(qwerty, dvorak) +to_qwerty = str.maketrans(dvorak, qwerty) + + +def translate(input, translation): + """ + >>> translate('dvorak', to_dvorak) + 'ekrpat' + >>> translate('qwerty', to_qwerty) + 'x,dokt' + """ + return input.translate(translation) + + +def _translate_stream(stream, translation): + """ + >>> import io + >>> _translate_stream(io.StringIO('foo'), to_dvorak) + urr + """ + print(translate(stream.read(), translation)) diff --git a/libs/win/jaraco/text/show-newlines.py b/libs/win/jaraco/text/show-newlines.py new file mode 100644 index 00000000..2ba32062 --- /dev/null +++ b/libs/win/jaraco/text/show-newlines.py @@ -0,0 +1,33 @@ +import autocommand +import inflect + +from more_itertools import always_iterable + +import jaraco.text + + +def report_newlines(filename): + r""" + Report the newlines in the indicated file. + + >>> tmp_path = getfixture('tmp_path') + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\nbar\n', newline='') + >>> report_newlines(filename) + newline is '\n' + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\nbar\r\n', newline='') + >>> report_newlines(filename) + newlines are ('\n', '\r\n') + """ + newlines = jaraco.text.read_newlines(filename) + count = len(tuple(always_iterable(newlines))) + engine = inflect.engine() + print( + engine.plural_noun("newline", count), + engine.plural_verb("is", count), + repr(newlines), + ) + + +autocommand.autocommand(__name__)(report_newlines) diff --git a/libs/win/jaraco/text/strip-prefix.py b/libs/win/jaraco/text/strip-prefix.py new file mode 100644 index 00000000..761717a9 --- /dev/null +++ b/libs/win/jaraco/text/strip-prefix.py @@ -0,0 +1,21 @@ +import sys + +import autocommand + +from jaraco.text import Stripper + + +def strip_prefix(): + r""" + Strip any common prefix from stdin. + + >>> import io, pytest + >>> getfixture('monkeypatch').setattr('sys.stdin', io.StringIO('abcdef\nabc123')) + >>> strip_prefix() + def + 123 + """ + sys.stdout.writelines(Stripper.strip_prefix(sys.stdin).lines) + + +autocommand.autocommand(__name__)(strip_prefix) diff --git a/libs/win/jaraco/text/to-dvorak.py b/libs/win/jaraco/text/to-dvorak.py new file mode 100644 index 00000000..a6d5da80 --- /dev/null +++ b/libs/win/jaraco/text/to-dvorak.py @@ -0,0 +1,6 @@ +import sys + +from . import layouts + + +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_dvorak) diff --git a/libs/win/jaraco/text/to-qwerty.py b/libs/win/jaraco/text/to-qwerty.py new file mode 100644 index 00000000..abe27286 --- /dev/null +++ b/libs/win/jaraco/text/to-qwerty.py @@ -0,0 +1,6 @@ +import sys + +from . import layouts + + +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_qwerty) diff --git a/libs/win/jaraco/ui/cmdline.py b/libs/win/jaraco/ui/cmdline.py index a7982ddb..8517af7a 100644 --- a/libs/win/jaraco/ui/cmdline.py +++ b/libs/win/jaraco/ui/cmdline.py @@ -1,77 +1,76 @@ import argparse -import six from jaraco.classes import meta -from jaraco import text +from jaraco import text # type: ignore -@six.add_metaclass(meta.LeafClassesMeta) -class Command(object): - """ - A general-purpose base class for creating commands for a command-line - program using argparse. Each subclass of Command represents a separate - sub-command of a program. +class Command(metaclass=meta.LeafClassesMeta): + """ + A general-purpose base class for creating commands for a command-line + program using argparse. Each subclass of Command represents a separate + sub-command of a program. - For example, one might use Command subclasses to implement the Mercurial - command set:: + For example, one might use Command subclasses to implement the Mercurial + command set:: - class Commit(Command): - @staticmethod - def add_arguments(cls, parser): - parser.add_argument('-m', '--message') + class Commit(Command): + @staticmethod + def add_arguments(cls, parser): + parser.add_argument('-m', '--message') - @classmethod - def run(cls, args): - "Run the 'commit' command with args (parsed)" + @classmethod + def run(cls, args): + "Run the 'commit' command with args (parsed)" - class Merge(Command): pass - class Pull(Command): pass - ... + class Merge(Command): pass + class Pull(Command): pass + ... - Then one could create an entry point for Mercurial like so:: + Then one could create an entry point for Mercurial like so:: - def hg_command(): - Command.invoke() - """ + def hg_command(): + Command.invoke() + """ - @classmethod - def add_subparsers(cls, parser): - subparsers = parser.add_subparsers() - [cmd_class.add_parser(subparsers) for cmd_class in cls._leaf_classes] + @classmethod + def add_subparsers(cls, parser): + subparsers = parser.add_subparsers() + [cmd_class.add_parser(subparsers) for cmd_class in cls._leaf_classes] - @classmethod - def add_parser(cls, subparsers): - cmd_string = text.words(cls.__name__).lowered().dash_separated() - parser = subparsers.add_parser(cmd_string) - parser.set_defaults(action=cls) - cls.add_arguments(parser) - return parser + @classmethod + def add_parser(cls, subparsers): + cmd_string = text.words(cls.__name__).lowered().dash_separated() + parser = subparsers.add_parser(cmd_string) + parser.set_defaults(action=cls) + cls.add_arguments(parser) + return parser - @classmethod - def add_arguments(cls, parser): - pass + @classmethod + def add_arguments(cls, parser): + pass - @classmethod - def invoke(cls): - """ - Invoke the command using ArgumentParser - """ - parser = argparse.ArgumentParser() - cls.add_subparsers(parser) - args = parser.parse_args() - args.action.run(args) + @classmethod + def invoke(cls): + """ + Invoke the command using ArgumentParser + """ + parser = argparse.ArgumentParser() + cls.add_subparsers(parser) + args = parser.parse_args() + args.action.run(args) class Extend(argparse.Action): - """ - Argparse action to take an nargs=* argument - and add any values to the existing value. + """ + Argparse action to take an nargs=* argument + and add any values to the existing value. - >>> parser = argparse.ArgumentParser() - >>> _ = parser.add_argument('--foo', nargs='*', default=[], action=Extend) - >>> args = parser.parse_args(['--foo', 'a=1', '--foo', 'b=2', 'c=3']) - >>> args.foo - ['a=1', 'b=2', 'c=3'] - """ - def __call__(self, parser, namespace, values, option_string=None): - getattr(namespace, self.dest).extend(values) + >>> parser = argparse.ArgumentParser() + >>> _ = parser.add_argument('--foo', nargs='*', default=[], action=Extend) + >>> args = parser.parse_args(['--foo', 'a=1', '--foo', 'b=2', 'c=3']) + >>> args.foo + ['a=1', 'b=2', 'c=3'] + """ + + def __call__(self, parser, namespace, values, option_string=None): + getattr(namespace, self.dest).extend(values) diff --git a/libs/win/jaraco/ui/editor.py b/libs/win/jaraco/ui/editor.py index b37c759d..c3102b64 100644 --- a/libs/win/jaraco/ui/editor.py +++ b/libs/win/jaraco/ui/editor.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals, absolute_import - import tempfile import os import sys @@ -9,100 +7,105 @@ import collections import io import difflib -import six +from typing import Mapping + + +class EditProcessException(RuntimeError): + pass -class EditProcessException(RuntimeError): pass class EditableFile(object): - """ - EditableFile saves some data to a temporary file, launches a - platform editor for interactive editing, and then reloads the data, - setting .changed to True if the data was edited. + """ + EditableFile saves some data to a temporary file, launches a + platform editor for interactive editing, and then reloads the data, + setting .changed to True if the data was edited. - e.g.:: + e.g.:: - x = EditableFile('foo') - x.edit() + x = EditableFile('foo') + x.edit() - if x.changed: - print(x.data) + if x.changed: + print(x.data) - The EDITOR environment variable can define which executable to use - (also XML_EDITOR if the content-type to edit includes 'xml'). If no - EDITOR is defined, defaults to 'notepad' on Windows and 'edit' on - other platforms. - """ - platform_default_editors = collections.defaultdict( - lambda: 'edit', - win32 = 'notepad', - linux2 = 'vi', - ) - encoding = 'utf-8' + The EDITOR environment variable can define which executable to use + (also XML_EDITOR if the content-type to edit includes 'xml'). If no + EDITOR is defined, defaults to 'notepad' on Windows and 'edit' on + other platforms. + """ - def __init__(self, data='', content_type='text/plain'): - self.data = six.text_type(data) - self.content_type = content_type + platform_default_editors: Mapping[str, str] = collections.defaultdict( + lambda: 'edit', + win32='notepad', + linux2='vi', + ) + encoding = 'utf-8' - def __enter__(self): - extension = mimetypes.guess_extension(self.content_type) or '' - fobj, self.name = tempfile.mkstemp(extension) - os.write(fobj, self.data.encode(self.encoding)) - os.close(fobj) - return self + def __init__(self, data='', content_type='text/plain'): + self.data = str(data) + self.content_type = content_type - def read(self): - with open(self.name, 'rb') as f: - return f.read().decode(self.encoding) + def __enter__(self): + extension = mimetypes.guess_extension(self.content_type) or '' + fobj, self.name = tempfile.mkstemp(extension) + os.write(fobj, self.data.encode(self.encoding)) + os.close(fobj) + return self - def __exit__(self, *tb_info): - os.remove(self.name) + def read(self): + with open(self.name, 'rb') as f: + return f.read().decode(self.encoding) - def edit(self): - """ - Edit the file - """ - self.changed = False - with self: - editor = self.get_editor() - cmd = [editor, self.name] - try: - res = subprocess.call(cmd) - except Exception as e: - print("Error launching editor %(editor)s" % locals()) - print(e) - return - if res != 0: - msg = '%(editor)s returned error status %(res)d' % locals() - raise EditProcessException(msg) - new_data = self.read() - if new_data != self.data: - self.changed = self._save_diff(self.data, new_data) - self.data = new_data + def __exit__(self, *tb_info): + os.remove(self.name) - @staticmethod - def _search_env(keys): - """ - Search the environment for the supplied keys, returning the first - one found or None if none was found. - """ - matches = (os.environ[key] for key in keys if key in os.environ) - return next(matches, None) + def edit(self): + """ + Edit the file + """ + self.changed = False + with self: + editor = self.get_editor() + cmd = [editor, self.name] + try: + res = subprocess.call(cmd) + except Exception as e: + print("Error launching editor %(editor)s" % locals()) + print(e) + return + if res != 0: + msg = '%(editor)s returned error status %(res)d' % locals() + raise EditProcessException(msg) + new_data = self.read() + if new_data != self.data: + self.changed = self._save_diff(self.data, new_data) + self.data = new_data - def get_editor(self): - """ - Give preference to an XML_EDITOR or EDITOR defined in the - environment. Otherwise use a default editor based on platform. - """ - env_search = ['EDITOR'] - if 'xml' in self.content_type: - env_search.insert(0, 'XML_EDITOR') - default_editor = self.platform_default_editors[sys.platform] - return self._search_env(env_search) or default_editor + @staticmethod + def _search_env(keys): + """ + Search the environment for the supplied keys, returning the first + one found or None if none was found. + """ + matches = (os.environ[key] for key in keys if key in os.environ) + return next(matches, None) - @staticmethod - def _save_diff(*versions): - def get_lines(content): - return list(io.StringIO(content)) - lines = map(get_lines, versions) - diff = difflib.context_diff(*lines) - return tuple(diff) + def get_editor(self): + """ + Give preference to an XML_EDITOR or EDITOR defined in the + environment. Otherwise use a default editor based on platform. + """ + env_search = ['EDITOR'] + if 'xml' in self.content_type: + env_search.insert(0, 'XML_EDITOR') + default_editor = self.platform_default_editors[sys.platform] + return self._search_env(env_search) or default_editor + + @staticmethod + def _save_diff(*versions): + def get_lines(content): + return list(io.StringIO(content)) + + lines = map(get_lines, versions) + diff = difflib.context_diff(*lines) + return tuple(diff) diff --git a/libs/win/jaraco/ui/input.py b/libs/win/jaraco/ui/input.py index 3d108fc0..c7b2c86a 100644 --- a/libs/win/jaraco/ui/input.py +++ b/libs/win/jaraco/ui/input.py @@ -3,24 +3,28 @@ This module currently provides a cross-platform getch function """ try: - # Windows - from msvcrt import getch + # Windows + from msvcrt import getch # type: ignore + + getch # workaround for https://github.com/kevinw/pyflakes/issues/13 except ImportError: - pass + pass try: - # Unix - import sys - import tty - import termios + # Unix + import sys + import tty + import termios + + def getch(): # type: ignore + fd = sys.stdin.fileno() + old = termios.tcgetattr(fd) + try: + tty.setraw(fd) + return sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old) + - def getch(): - fd = sys.stdin.fileno() - old = termios.tcgetattr(fd) - try: - tty.setraw(fd) - return sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old) except ImportError: - pass + pass diff --git a/libs/win/jaraco/ui/menu.py b/libs/win/jaraco/ui/menu.py index aede93b3..78687b3f 100644 --- a/libs/win/jaraco/ui/menu.py +++ b/libs/win/jaraco/ui/menu.py @@ -1,34 +1,32 @@ -from __future__ import print_function, absolute_import, unicode_literals - import itertools -import six class Menu(object): - """ - A simple command-line based menu - """ - def __init__(self, choices=None, formatter=str): - self.choices = choices or list() - self.formatter = formatter + """ + A simple command-line based menu + """ - def get_choice(self, prompt="> "): - n = len(self.choices) - number_width = len(str(n)) + 1 - menu_fmt = '{number:{number_width}}) {choice}' - formatted_choices = map(self.formatter, self.choices) - for number, choice in zip(itertools.count(1), formatted_choices): - print(menu_fmt.format(**locals())) - print() - try: - answer = int(six.moves.input(prompt)) - result = self.choices[answer - 1] - except ValueError: - print('invalid selection') - result = None - except IndexError: - print('invalid selection') - result = None - except KeyboardInterrupt: - result = None - return result + def __init__(self, choices=None, formatter=str): + self.choices = choices or list() + self.formatter = formatter + + def get_choice(self, prompt="> "): + n = len(self.choices) + number_width = len(str(n)) + 1 + menu_fmt = '{number:{number_width}}) {choice}' + formatted_choices = map(self.formatter, self.choices) + for number, choice in zip(itertools.count(1), formatted_choices): + print(menu_fmt.format(**locals())) + print() + try: + answer = int(input(prompt)) + result = self.choices[answer - 1] + except ValueError: + print('invalid selection') + result = None + except IndexError: + print('invalid selection') + result = None + except KeyboardInterrupt: + result = None + return result diff --git a/libs/win/jaraco/ui/progress.py b/libs/win/jaraco/ui/progress.py index d083310b..b57b1c85 100644 --- a/libs/win/jaraco/ui/progress.py +++ b/libs/win/jaraco/ui/progress.py @@ -1,152 +1,141 @@ # deprecated -- use TQDM -from __future__ import (print_function, absolute_import, unicode_literals, - division) - import time import sys import itertools import abc import datetime -import six +class AbstractProgressBar(metaclass=abc.ABCMeta): + def __init__(self, unit='', size=70): + """ + Size is the nominal size in characters + """ + self.unit = unit + self.size = size -@six.add_metaclass(abc.ABCMeta) -class AbstractProgressBar(object): - def __init__(self, unit='', size=70): - """ - Size is the nominal size in characters - """ - self.unit = unit - self.size = size + def report(self, amt): + sys.stdout.write('\r%s' % self.get_bar(amt)) + sys.stdout.flush() - def report(self, amt): - sys.stdout.write('\r%s' % self.get_bar(amt)) - sys.stdout.flush() + @abc.abstractmethod + def get_bar(self, amt): + "Return the string to be printed. Should be size >= self.size" - @abc.abstractmethod - def get_bar(self, amt): - "Return the string to be printed. Should be size >= self.size" + def summary(self, str): + return ' (' + self.unit_str(str) + ')' - def summary(self, str): - return ' (' + self.unit_str(str) + ')' + def unit_str(self, str): + if self.unit: + str += ' ' + self.unit + return str - def unit_str(self, str): - if self.unit: - str += ' ' + self.unit - return str + def finish(self): + print() - def finish(self): - print() + def __enter__(self): + self.report(0) + return self - def __enter__(self): - self.report(0) - return self + def __exit__(self, exc, exc_val, tb): + if exc is None: + self.finish() + else: + print() - def __exit__(self, exc, exc_val, tb): - if exc is None: - self.finish() - else: - print() - - def iterate(self, iterable): - """ - Report the status as the iterable is consumed. - """ - with self: - for n, item in enumerate(iterable, 1): - self.report(n) - yield item + def iterate(self, iterable): + """ + Report the status as the iterable is consumed. + """ + with self: + for n, item in enumerate(iterable, 1): + self.report(n) + yield item class SimpleProgressBar(AbstractProgressBar): - _PROG_DISPGLYPH = itertools.cycle(['|', '/', '-', '\\']) + _PROG_DISPGLYPH = itertools.cycle(['|', '/', '-', '\\']) - def get_bar(self, amt): - bar = next(self._PROG_DISPGLYPH) - template = ' [{bar:^{bar_len}}]' - summary = self.summary('{amt}') - template += summary - empty = template.format( - bar='', - bar_len=0, - amt=amt, - ) - bar_len = self.size - len(empty) - return template.format(**locals()) + def get_bar(self, amt): + bar = next(self._PROG_DISPGLYPH) + template = ' [{bar:^{bar_len}}]' + summary = self.summary('{amt}') + template += summary + empty = template.format( + bar='', + bar_len=0, + amt=amt, + ) + bar_len = self.size - len(empty) + return template.format(**locals()) - @classmethod - def demo(cls): - bar3 = cls(unit='cubes', size=30) - with bar3: - for x in six.moves.range(1, 759): - bar3.report(x) - time.sleep(0.01) + @classmethod + def demo(cls): + bar3 = cls(unit='cubes', size=30) + with bar3: + for x in range(1, 759): + bar3.report(x) + time.sleep(0.01) class TargetProgressBar(AbstractProgressBar): - def __init__(self, total=None, unit='', size=70): - """ - Size is the nominal size in characters - """ - self.total = total - super(TargetProgressBar, self).__init__(unit, size) + def __init__(self, total=None, unit='', size=70): + """ + Size is the nominal size in characters + """ + self.total = total + super(TargetProgressBar, self).__init__(unit, size) - def get_bar(self, amt): - template = ' [{bar:<{bar_len}}]' - completed = amt / self.total - percent = int(completed * 100) - percent_str = ' {percent:3}%' - template += percent_str - summary = self.summary('{amt}/{total}') - template += summary - empty = template.format( - total=self.total, - bar='', - bar_len=0, - **locals() - ) - bar_len = self.size - len(empty) - bar = '=' * int(completed * bar_len) - return template.format(total=self.total, **locals()) + def get_bar(self, amt): + template = ' [{bar:<{bar_len}}]' + completed = amt / self.total + percent = int(completed * 100) + percent_str = ' {percent:3}%' + template += percent_str + summary = self.summary('{amt}/{total}') + template += summary + empty = template.format(total=self.total, bar='', bar_len=0, **locals()) + bar_len = self.size - len(empty) + bar = '=' * int(completed * bar_len) + return template.format(total=self.total, **locals()) - @classmethod - def demo(cls): - bar1 = cls(100, 'blocks') - with bar1: - for x in six.moves.range(1, 101): - bar1.report(x) - time.sleep(0.05) + @classmethod + def demo(cls): + bar1 = cls(100, 'blocks') + with bar1: + for x in range(1, 101): + bar1.report(x) + time.sleep(0.05) - bar2 = cls(758, size=50) - with bar2: - for x in six.moves.range(1, 759): - bar2.report(x) - time.sleep(0.01) + bar2 = cls(758, size=50) + with bar2: + for x in range(1, 759): + bar2.report(x) + time.sleep(0.01) - def finish(self): - self.report(self.total) - super(TargetProgressBar, self).finish() + def finish(self): + self.report(self.total) + super(TargetProgressBar, self).finish() def countdown(template, duration=datetime.timedelta(seconds=5)): - """ - Do a countdown for duration, printing the template (which may accept one - positional argument). Template should be something like - ``countdown complete in {} seconds.`` - """ - now = datetime.datetime.now() - deadline = now + duration - remaining = deadline - datetime.datetime.now() - while remaining: - remaining = deadline - datetime.datetime.now() - remaining = max(datetime.timedelta(), remaining) - msg = template.format(remaining.total_seconds()) - print(msg, end=' '*10) - sys.stdout.flush() - time.sleep(.1) - print('\b'*80, end='') - sys.stdout.flush() - print() + """ + Do a countdown for duration, printing the template (which may accept one + positional argument). Template should be something like + ``countdown complete in {} seconds.`` + """ + now = datetime.datetime.now() + deadline = now + duration + remaining = deadline - datetime.datetime.now() + while remaining: + remaining = deadline - datetime.datetime.now() + remaining = max(datetime.timedelta(), remaining) + msg = template.format(remaining.total_seconds()) + print(msg, end=' ' * 10) + sys.stdout.flush() + time.sleep(0.1) + print('\b' * 80, end='') + sys.stdout.flush() + print() diff --git a/libs/win/jaraco/windows/api/clipboard.py b/libs/win/jaraco/windows/api/clipboard.py index d871aaa9..80a751f8 100644 --- a/libs/win/jaraco/windows/api/clipboard.py +++ b/libs/win/jaraco/windows/api/clipboard.py @@ -30,16 +30,16 @@ CF_GDIOBJFIRST = 0x0300 CF_GDIOBJLAST = 0x03FF RegisterClipboardFormat = ctypes.windll.user32.RegisterClipboardFormatW -RegisterClipboardFormat.argtypes = ctypes.wintypes.LPWSTR, +RegisterClipboardFormat.argtypes = (ctypes.wintypes.LPWSTR,) RegisterClipboardFormat.restype = ctypes.wintypes.UINT CF_HTML = RegisterClipboardFormat('HTML Format') EnumClipboardFormats = ctypes.windll.user32.EnumClipboardFormats -EnumClipboardFormats.argtypes = ctypes.wintypes.UINT, +EnumClipboardFormats.argtypes = (ctypes.wintypes.UINT,) EnumClipboardFormats.restype = ctypes.wintypes.UINT GetClipboardData = ctypes.windll.user32.GetClipboardData -GetClipboardData.argtypes = ctypes.wintypes.UINT, +GetClipboardData.argtypes = (ctypes.wintypes.UINT,) GetClipboardData.restype = ctypes.wintypes.HANDLE SetClipboardData = ctypes.windll.user32.SetClipboardData @@ -47,7 +47,7 @@ SetClipboardData.argtypes = ctypes.wintypes.UINT, ctypes.wintypes.HANDLE SetClipboardData.restype = ctypes.wintypes.HANDLE OpenClipboard = ctypes.windll.user32.OpenClipboard -OpenClipboard.argtypes = ctypes.wintypes.HANDLE, +OpenClipboard.argtypes = (ctypes.wintypes.HANDLE,) OpenClipboard.restype = ctypes.wintypes.BOOL ctypes.windll.user32.CloseClipboard.restype = ctypes.wintypes.BOOL diff --git a/libs/win/jaraco/windows/api/credential.py b/libs/win/jaraco/windows/api/credential.py index 003c3cb3..db1deb9e 100644 --- a/libs/win/jaraco/windows/api/credential.py +++ b/libs/win/jaraco/windows/api/credential.py @@ -5,58 +5,52 @@ Support for Credential Vault import ctypes from ctypes.wintypes import DWORD, LPCWSTR, BOOL, LPWSTR, FILETIME + try: - from ctypes.wintypes import LPBYTE + from ctypes.wintypes import LPBYTE except ImportError: - LPBYTE = ctypes.POINTER(ctypes.wintypes.BYTE) + LPBYTE = ctypes.POINTER(ctypes.wintypes.BYTE) # type: ignore class CredentialAttribute(ctypes.Structure): - _fields_ = [] + _fields_ = [] # type: ignore class Credential(ctypes.Structure): - _fields_ = [ - ('flags', DWORD), - ('type', DWORD), - ('target_name', LPWSTR), - ('comment', LPWSTR), - ('last_written', FILETIME), - ('credential_blob_size', DWORD), - ('credential_blob', LPBYTE), - ('persist', DWORD), - ('attribute_count', DWORD), - ('attributes', ctypes.POINTER(CredentialAttribute)), - ('target_alias', LPWSTR), - ('user_name', LPWSTR), - ] + _fields_ = [ + ('flags', DWORD), + ('type', DWORD), + ('target_name', LPWSTR), + ('comment', LPWSTR), + ('last_written', FILETIME), + ('credential_blob_size', DWORD), + ('credential_blob', LPBYTE), + ('persist', DWORD), + ('attribute_count', DWORD), + ('attributes', ctypes.POINTER(CredentialAttribute)), + ('target_alias', LPWSTR), + ('user_name', LPWSTR), + ] - def __del__(self): - ctypes.windll.advapi32.CredFree(ctypes.byref(self)) + def __del__(self): + ctypes.windll.advapi32.CredFree(ctypes.byref(self)) PCREDENTIAL = ctypes.POINTER(Credential) CredRead = ctypes.windll.advapi32.CredReadW CredRead.argtypes = ( - LPCWSTR, # TargetName - DWORD, # Type - DWORD, # Flags - ctypes.POINTER(PCREDENTIAL), # Credential + LPCWSTR, # TargetName + DWORD, # Type + DWORD, # Flags + ctypes.POINTER(PCREDENTIAL), # Credential ) CredRead.restype = BOOL CredWrite = ctypes.windll.advapi32.CredWriteW -CredWrite.argtypes = ( - PCREDENTIAL, # Credential - DWORD, # Flags -) +CredWrite.argtypes = (PCREDENTIAL, DWORD) # Credential # Flags CredWrite.restype = BOOL CredDelete = ctypes.windll.advapi32.CredDeleteW -CredDelete.argtypes = ( - LPCWSTR, # TargetName - DWORD, # Type - DWORD, # Flags -) +CredDelete.argtypes = (LPCWSTR, DWORD, DWORD) # TargetName # Type # Flags CredDelete.restype = BOOL diff --git a/libs/win/jaraco/windows/api/environ.py b/libs/win/jaraco/windows/api/environ.py index f394da02..a7882364 100644 --- a/libs/win/jaraco/windows/api/environ.py +++ b/libs/win/jaraco/windows/api/environ.py @@ -7,7 +7,7 @@ SetEnvironmentVariable.argtypes = [ctypes.wintypes.LPCWSTR] * 2 GetEnvironmentVariable = ctypes.windll.kernel32.GetEnvironmentVariableW GetEnvironmentVariable.restype = ctypes.wintypes.BOOL GetEnvironmentVariable.argtypes = [ - ctypes.wintypes.LPCWSTR, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.DWORD, + ctypes.wintypes.LPCWSTR, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, ] diff --git a/libs/win/jaraco/windows/api/event.py b/libs/win/jaraco/windows/api/event.py index 5d2818c6..5b5d9f9d 100644 --- a/libs/win/jaraco/windows/api/event.py +++ b/libs/win/jaraco/windows/api/event.py @@ -1,19 +1,12 @@ from ctypes import windll, POINTER -from ctypes.wintypes import ( - LPWSTR, DWORD, LPVOID, HANDLE, BOOL, -) +from ctypes.wintypes import LPWSTR, DWORD, LPVOID, HANDLE, BOOL CreateEvent = windll.kernel32.CreateEventW -CreateEvent.argtypes = ( - LPVOID, # LPSECURITY_ATTRIBUTES - BOOL, - BOOL, - LPWSTR, -) +CreateEvent.argtypes = (LPVOID, BOOL, BOOL, LPWSTR) # LPSECURITY_ATTRIBUTES CreateEvent.restype = HANDLE SetEvent = windll.kernel32.SetEvent -SetEvent.argtypes = HANDLE, +SetEvent.argtypes = (HANDLE,) SetEvent.restype = BOOL WaitForSingleObject = windll.kernel32.WaitForSingleObject @@ -26,11 +19,11 @@ _WaitForMultipleObjects.restype = DWORD def WaitForMultipleObjects(handles, wait_all=False, timeout=0): - n_handles = len(handles) - handle_array = (HANDLE * n_handles)() - for index, handle in enumerate(handles): - handle_array[index] = handle - return _WaitForMultipleObjects(n_handles, handle_array, wait_all, timeout) + n_handles = len(handles) + handle_array = (HANDLE * n_handles)() + for index, handle in enumerate(handles): + handle_array[index] = handle + return _WaitForMultipleObjects(n_handles, handle_array, wait_all, timeout) WAIT_OBJECT_0 = 0 diff --git a/libs/win/jaraco/windows/api/filesystem.py b/libs/win/jaraco/windows/api/filesystem.py index fbd999de..b06dc6d2 100644 --- a/libs/win/jaraco/windows/api/filesystem.py +++ b/libs/win/jaraco/windows/api/filesystem.py @@ -2,22 +2,24 @@ import ctypes.wintypes CreateSymbolicLink = ctypes.windll.kernel32.CreateSymbolicLinkW CreateSymbolicLink.argtypes = ( - ctypes.wintypes.LPWSTR, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.DWORD, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, ) CreateSymbolicLink.restype = ctypes.wintypes.BOOLEAN +SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2 + CreateHardLink = ctypes.windll.kernel32.CreateHardLinkW CreateHardLink.argtypes = ( - ctypes.wintypes.LPWSTR, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.LPVOID, # reserved for LPSECURITY_ATTRIBUTES + ctypes.wintypes.LPWSTR, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.LPVOID, # reserved for LPSECURITY_ATTRIBUTES ) CreateHardLink.restype = ctypes.wintypes.BOOLEAN GetFileAttributes = ctypes.windll.kernel32.GetFileAttributesW -GetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, +GetFileAttributes.argtypes = (ctypes.wintypes.LPWSTR,) GetFileAttributes.restype = ctypes.wintypes.DWORD SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW @@ -28,31 +30,33 @@ MAX_PATH = 260 GetFinalPathNameByHandle = ctypes.windll.kernel32.GetFinalPathNameByHandleW GetFinalPathNameByHandle.argtypes = ( - ctypes.wintypes.HANDLE, ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD, - ctypes.wintypes.DWORD, + ctypes.wintypes.HANDLE, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, ) GetFinalPathNameByHandle.restype = ctypes.wintypes.DWORD class SECURITY_ATTRIBUTES(ctypes.Structure): - _fields_ = ( - ('length', ctypes.wintypes.DWORD), - ('p_security_descriptor', ctypes.wintypes.LPVOID), - ('inherit_handle', ctypes.wintypes.BOOLEAN), - ) + _fields_ = ( + ('length', ctypes.wintypes.DWORD), + ('p_security_descriptor', ctypes.wintypes.LPVOID), + ('inherit_handle', ctypes.wintypes.BOOLEAN), + ) LPSECURITY_ATTRIBUTES = ctypes.POINTER(SECURITY_ATTRIBUTES) CreateFile = ctypes.windll.kernel32.CreateFileW CreateFile.argtypes = ( - ctypes.wintypes.LPWSTR, - ctypes.wintypes.DWORD, - ctypes.wintypes.DWORD, - LPSECURITY_ATTRIBUTES, - ctypes.wintypes.DWORD, - ctypes.wintypes.DWORD, - ctypes.wintypes.HANDLE, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + LPSECURITY_ATTRIBUTES, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ctypes.wintypes.HANDLE, ) CreateFile.restype = ctypes.wintypes.HANDLE FILE_SHARE_READ = 1 @@ -83,56 +87,56 @@ CloseHandle.restype = ctypes.wintypes.BOOLEAN class WIN32_FIND_DATA(ctypes.wintypes.WIN32_FIND_DATAW): - """ - _fields_ = [ - ("dwFileAttributes", DWORD), - ("ftCreationTime", FILETIME), - ("ftLastAccessTime", FILETIME), - ("ftLastWriteTime", FILETIME), - ("nFileSizeHigh", DWORD), - ("nFileSizeLow", DWORD), - ("dwReserved0", DWORD), - ("dwReserved1", DWORD), - ("cFileName", WCHAR * MAX_PATH), - ("cAlternateFileName", WCHAR * 14)] - ] - """ + """ + _fields_ = [ + ("dwFileAttributes", DWORD), + ("ftCreationTime", FILETIME), + ("ftLastAccessTime", FILETIME), + ("ftLastWriteTime", FILETIME), + ("nFileSizeHigh", DWORD), + ("nFileSizeLow", DWORD), + ("dwReserved0", DWORD), + ("dwReserved1", DWORD), + ("cFileName", WCHAR * MAX_PATH), + ("cAlternateFileName", WCHAR * 14)] + ] + """ - @property - def file_attributes(self): - return self.dwFileAttributes + @property + def file_attributes(self): + return self.dwFileAttributes - @property - def creation_time(self): - return self.ftCreationTime + @property + def creation_time(self): + return self.ftCreationTime - @property - def last_access_time(self): - return self.ftLastAccessTime + @property + def last_access_time(self): + return self.ftLastAccessTime - @property - def last_write_time(self): - return self.ftLastWriteTime + @property + def last_write_time(self): + return self.ftLastWriteTime - @property - def file_size_words(self): - return [self.nFileSizeHigh, self.nFileSizeLow] + @property + def file_size_words(self): + return [self.nFileSizeHigh, self.nFileSizeLow] - @property - def reserved(self): - return [self.dwReserved0, self.dwReserved1] + @property + def reserved(self): + return [self.dwReserved0, self.dwReserved1] - @property - def filename(self): - return self.cFileName + @property + def filename(self): + return self.cFileName - @property - def alternate_filename(self): - return self.cAlternateFileName + @property + def alternate_filename(self): + return self.cAlternateFileName - @property - def file_size(self): - return self.nFileSizeHigh << 32 + self.nFileSizeLow + @property + def file_size(self): + return self.nFileSizeHigh << 32 + self.nFileSizeLow LPWIN32_FIND_DATA = ctypes.POINTER(ctypes.wintypes.WIN32_FIND_DATAW) @@ -144,7 +148,7 @@ FindNextFile = ctypes.windll.kernel32.FindNextFileW FindNextFile.argtypes = (ctypes.wintypes.HANDLE, LPWIN32_FIND_DATA) FindNextFile.restype = ctypes.wintypes.BOOLEAN -ctypes.windll.kernel32.FindClose.argtypes = ctypes.wintypes.HANDLE, +ctypes.windll.kernel32.FindClose.argtypes = (ctypes.wintypes.HANDLE,) SCS_32BIT_BINARY = 0 # A 32-bit Windows-based application SCS_64BIT_BINARY = 6 # A 64-bit Windows-based application @@ -156,7 +160,8 @@ SCS_WOW_BINARY = 2 # A 16-bit Windows-based application _GetBinaryType = ctypes.windll.kernel32.GetBinaryTypeW _GetBinaryType.argtypes = ( - ctypes.wintypes.LPWSTR, ctypes.POINTER(ctypes.wintypes.DWORD), + ctypes.wintypes.LPWSTR, + ctypes.POINTER(ctypes.wintypes.DWORD), ) _GetBinaryType.restype = ctypes.wintypes.BOOL @@ -164,47 +169,47 @@ FILEOP_FLAGS = ctypes.wintypes.WORD class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): - _fields_ = [ - ('file_attributes', ctypes.wintypes.DWORD), - ('creation_time', ctypes.wintypes.FILETIME), - ('last_access_time', ctypes.wintypes.FILETIME), - ('last_write_time', ctypes.wintypes.FILETIME), - ('volume_serial_number', ctypes.wintypes.DWORD), - ('file_size_high', ctypes.wintypes.DWORD), - ('file_size_low', ctypes.wintypes.DWORD), - ('number_of_links', ctypes.wintypes.DWORD), - ('file_index_high', ctypes.wintypes.DWORD), - ('file_index_low', ctypes.wintypes.DWORD), - ] + _fields_ = [ + ('file_attributes', ctypes.wintypes.DWORD), + ('creation_time', ctypes.wintypes.FILETIME), + ('last_access_time', ctypes.wintypes.FILETIME), + ('last_write_time', ctypes.wintypes.FILETIME), + ('volume_serial_number', ctypes.wintypes.DWORD), + ('file_size_high', ctypes.wintypes.DWORD), + ('file_size_low', ctypes.wintypes.DWORD), + ('number_of_links', ctypes.wintypes.DWORD), + ('file_index_high', ctypes.wintypes.DWORD), + ('file_index_low', ctypes.wintypes.DWORD), + ] - @property - def file_size(self): - return (self.file_size_high << 32) + self.file_size_low + @property + def file_size(self): + return (self.file_size_high << 32) + self.file_size_low - @property - def file_index(self): - return (self.file_index_high << 32) + self.file_index_low + @property + def file_index(self): + return (self.file_index_high << 32) + self.file_index_low GetFileInformationByHandle = ctypes.windll.kernel32.GetFileInformationByHandle GetFileInformationByHandle.restype = ctypes.wintypes.BOOL GetFileInformationByHandle.argtypes = ( - ctypes.wintypes.HANDLE, - ctypes.POINTER(BY_HANDLE_FILE_INFORMATION), + ctypes.wintypes.HANDLE, + ctypes.POINTER(BY_HANDLE_FILE_INFORMATION), ) class SHFILEOPSTRUCT(ctypes.Structure): - _fields_ = [ - ('status_dialog', ctypes.wintypes.HWND), - ('operation', ctypes.wintypes.UINT), - ('from_', ctypes.wintypes.LPWSTR), - ('to', ctypes.wintypes.LPWSTR), - ('flags', FILEOP_FLAGS), - ('operations_aborted', ctypes.wintypes.BOOL), - ('name_mapping_handles', ctypes.wintypes.LPVOID), - ('progress_title', ctypes.wintypes.LPWSTR), - ] + _fields_ = [ + ('status_dialog', ctypes.wintypes.HWND), + ('operation', ctypes.wintypes.UINT), + ('from_', ctypes.wintypes.LPWSTR), + ('to', ctypes.wintypes.LPWSTR), + ('flags', FILEOP_FLAGS), + ('operations_aborted', ctypes.wintypes.BOOL), + ('name_mapping_handles', ctypes.wintypes.LPVOID), + ('progress_title', ctypes.wintypes.LPWSTR), + ] _SHFileOperation = ctypes.windll.shell32.SHFileOperationW @@ -218,12 +223,12 @@ FO_DELETE = 3 ReplaceFile = ctypes.windll.kernel32.ReplaceFileW ReplaceFile.restype = ctypes.wintypes.BOOL ReplaceFile.argtypes = [ - ctypes.wintypes.LPWSTR, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.DWORD, - ctypes.wintypes.LPVOID, - ctypes.wintypes.LPVOID, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, + ctypes.wintypes.LPVOID, + ctypes.wintypes.LPVOID, ] REPLACEFILE_WRITE_THROUGH = 0x1 @@ -232,20 +237,20 @@ REPLACEFILE_IGNORE_ACL_ERRORS = 0x4 class STAT_STRUCT(ctypes.Structure): - _fields_ = [ - ('dev', ctypes.c_uint), - ('ino', ctypes.c_ushort), - ('mode', ctypes.c_ushort), - ('nlink', ctypes.c_short), - ('uid', ctypes.c_short), - ('gid', ctypes.c_short), - ('rdev', ctypes.c_uint), - # the following 4 fields are ctypes.c_uint64 for _stat64 - ('size', ctypes.c_uint), - ('atime', ctypes.c_uint), - ('mtime', ctypes.c_uint), - ('ctime', ctypes.c_uint), - ] + _fields_ = [ + ('dev', ctypes.c_uint), + ('ino', ctypes.c_ushort), + ('mode', ctypes.c_ushort), + ('nlink', ctypes.c_short), + ('uid', ctypes.c_short), + ('gid', ctypes.c_short), + ('rdev', ctypes.c_uint), + # the following 4 fields are ctypes.c_uint64 for _stat64 + ('size', ctypes.c_uint), + ('atime', ctypes.c_uint), + ('mtime', ctypes.c_uint), + ('ctime', ctypes.c_uint), + ] _wstat = ctypes.windll.msvcrt._wstat @@ -254,64 +259,64 @@ _wstat.restype = ctypes.c_int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10 -FindFirstChangeNotification = ( - ctypes.windll.kernel32.FindFirstChangeNotificationW) +FindFirstChangeNotification = ctypes.windll.kernel32.FindFirstChangeNotificationW FindFirstChangeNotification.argtypes = ( - ctypes.wintypes.LPWSTR, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.BOOL, + ctypes.wintypes.DWORD, ) FindFirstChangeNotification.restype = ctypes.wintypes.HANDLE -FindCloseChangeNotification = ( - ctypes.windll.kernel32.FindCloseChangeNotification) -FindCloseChangeNotification.argtypes = ctypes.wintypes.HANDLE, +FindCloseChangeNotification = ctypes.windll.kernel32.FindCloseChangeNotification +FindCloseChangeNotification.argtypes = (ctypes.wintypes.HANDLE,) FindCloseChangeNotification.restype = ctypes.wintypes.BOOL FindNextChangeNotification = ctypes.windll.kernel32.FindNextChangeNotification -FindNextChangeNotification.argtypes = ctypes.wintypes.HANDLE, +FindNextChangeNotification.argtypes = (ctypes.wintypes.HANDLE,) FindNextChangeNotification.restype = ctypes.wintypes.BOOL FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 IO_REPARSE_TAG_SYMLINK = 0xA000000C -FSCTL_GET_REPARSE_POINT = 0x900a8 +FSCTL_GET_REPARSE_POINT = 0x900A8 LPDWORD = ctypes.POINTER(ctypes.wintypes.DWORD) LPOVERLAPPED = ctypes.wintypes.LPVOID DeviceIoControl = ctypes.windll.kernel32.DeviceIoControl DeviceIoControl.argtypes = [ - ctypes.wintypes.HANDLE, - ctypes.wintypes.DWORD, - ctypes.wintypes.LPVOID, - ctypes.wintypes.DWORD, - ctypes.wintypes.LPVOID, - ctypes.wintypes.DWORD, - LPDWORD, - LPOVERLAPPED, + ctypes.wintypes.HANDLE, + ctypes.wintypes.DWORD, + ctypes.wintypes.LPVOID, + ctypes.wintypes.DWORD, + ctypes.wintypes.LPVOID, + ctypes.wintypes.DWORD, + LPDWORD, + LPOVERLAPPED, ] DeviceIoControl.restype = ctypes.wintypes.BOOL class REPARSE_DATA_BUFFER(ctypes.Structure): - _fields_ = [ - ('tag', ctypes.c_ulong), - ('data_length', ctypes.c_ushort), - ('reserved', ctypes.c_ushort), - ('substitute_name_offset', ctypes.c_ushort), - ('substitute_name_length', ctypes.c_ushort), - ('print_name_offset', ctypes.c_ushort), - ('print_name_length', ctypes.c_ushort), - ('flags', ctypes.c_ulong), - ('path_buffer', ctypes.c_byte * 1), - ] + _fields_ = [ + ('tag', ctypes.c_ulong), + ('data_length', ctypes.c_ushort), + ('reserved', ctypes.c_ushort), + ('substitute_name_offset', ctypes.c_ushort), + ('substitute_name_length', ctypes.c_ushort), + ('print_name_offset', ctypes.c_ushort), + ('print_name_length', ctypes.c_ushort), + ('flags', ctypes.c_ulong), + ('path_buffer', ctypes.c_byte * 1), + ] - def get_print_name(self): - wchar_size = ctypes.sizeof(ctypes.wintypes.WCHAR) - arr_typ = ctypes.wintypes.WCHAR * (self.print_name_length // wchar_size) - data = ctypes.byref(self.path_buffer, self.print_name_offset) - return ctypes.cast(data, ctypes.POINTER(arr_typ)).contents.value + def get_print_name(self): + wchar_size = ctypes.sizeof(ctypes.wintypes.WCHAR) + arr_typ = ctypes.wintypes.WCHAR * (self.print_name_length // wchar_size) + data = ctypes.byref(self.path_buffer, self.print_name_offset) + return ctypes.cast(data, ctypes.POINTER(arr_typ)).contents.value - def get_substitute_name(self): - wchar_size = ctypes.sizeof(ctypes.wintypes.WCHAR) - arr_typ = ctypes.wintypes.WCHAR * (self.substitute_name_length // wchar_size) - data = ctypes.byref(self.path_buffer, self.substitute_name_offset) - return ctypes.cast(data, ctypes.POINTER(arr_typ)).contents.value + def get_substitute_name(self): + wchar_size = ctypes.sizeof(ctypes.wintypes.WCHAR) + arr_typ = ctypes.wintypes.WCHAR * (self.substitute_name_length // wchar_size) + data = ctypes.byref(self.path_buffer, self.substitute_name_offset) + return ctypes.cast(data, ctypes.POINTER(arr_typ)).contents.value diff --git a/libs/win/jaraco/windows/api/inet.py b/libs/win/jaraco/windows/api/inet.py index 36c8e37c..f144b0f3 100644 --- a/libs/win/jaraco/windows/api/inet.py +++ b/libs/win/jaraco/windows/api/inet.py @@ -4,11 +4,11 @@ from ctypes.wintypes import DWORD, WCHAR, BYTE, BOOL # from mprapi.h -MAX_INTERFACE_NAME_LEN = 2**8 +MAX_INTERFACE_NAME_LEN = 2 ** 8 # from iprtrmib.h -MAXLEN_PHYSADDR = 2**3 -MAXLEN_IFDESCR = 2**8 +MAXLEN_PHYSADDR = 2 ** 3 +MAXLEN_IFDESCR = 2 ** 8 # from iptypes.h MAX_ADAPTER_ADDRESS_LENGTH = 8 @@ -16,114 +16,102 @@ MAX_DHCPV6_DUID_LENGTH = 130 class MIB_IFROW(ctypes.Structure): - _fields_ = ( - ('name', WCHAR * MAX_INTERFACE_NAME_LEN), - ('index', DWORD), - ('type', DWORD), - ('MTU', DWORD), - ('speed', DWORD), - ('physical_address_length', DWORD), - ('physical_address_raw', BYTE * MAXLEN_PHYSADDR), - ('admin_status', DWORD), - ('operational_status', DWORD), - ('last_change', DWORD), - ('octets_received', DWORD), - ('unicast_packets_received', DWORD), - ('non_unicast_packets_received', DWORD), - ('incoming_discards', DWORD), - ('incoming_errors', DWORD), - ('incoming_unknown_protocols', DWORD), - ('octets_sent', DWORD), - ('unicast_packets_sent', DWORD), - ('non_unicast_packets_sent', DWORD), - ('outgoing_discards', DWORD), - ('outgoing_errors', DWORD), - ('outgoing_queue_length', DWORD), - ('description_length', DWORD), - ('description_raw', ctypes.c_char * MAXLEN_IFDESCR), - ) + _fields_ = ( + ('name', WCHAR * MAX_INTERFACE_NAME_LEN), + ('index', DWORD), + ('type', DWORD), + ('MTU', DWORD), + ('speed', DWORD), + ('physical_address_length', DWORD), + ('physical_address_raw', BYTE * MAXLEN_PHYSADDR), + ('admin_status', DWORD), + ('operational_status', DWORD), + ('last_change', DWORD), + ('octets_received', DWORD), + ('unicast_packets_received', DWORD), + ('non_unicast_packets_received', DWORD), + ('incoming_discards', DWORD), + ('incoming_errors', DWORD), + ('incoming_unknown_protocols', DWORD), + ('octets_sent', DWORD), + ('unicast_packets_sent', DWORD), + ('non_unicast_packets_sent', DWORD), + ('outgoing_discards', DWORD), + ('outgoing_errors', DWORD), + ('outgoing_queue_length', DWORD), + ('description_length', DWORD), + ('description_raw', ctypes.c_char * MAXLEN_IFDESCR), + ) - def _get_binary_property(self, name): - val_prop = '{0}_raw'.format(name) - val = getattr(self, val_prop) - len_prop = '{0}_length'.format(name) - length = getattr(self, len_prop) - return str(memoryview(val))[:length] + def _get_binary_property(self, name): + val_prop = '{0}_raw'.format(name) + val = getattr(self, val_prop) + len_prop = '{0}_length'.format(name) + length = getattr(self, len_prop) + return str(memoryview(val))[:length] - @property - def physical_address(self): - return self._get_binary_property('physical_address') + @property + def physical_address(self): + return self._get_binary_property('physical_address') - @property - def description(self): - return self._get_binary_property('description') + @property + def description(self): + return self._get_binary_property('description') class MIB_IFTABLE(ctypes.Structure): - _fields_ = ( - ('num_entries', DWORD), # dwNumEntries - ('entries', MIB_IFROW * 0), # table - ) + _fields_ = ( + ('num_entries', DWORD), # dwNumEntries + ('entries', MIB_IFROW * 0), # table + ) class MIB_IPADDRROW(ctypes.Structure): - _fields_ = ( - ('address_num', DWORD), - ('index', DWORD), - ('mask', DWORD), - ('broadcast_address', DWORD), - ('reassembly_size', DWORD), - ('unused', ctypes.c_ushort), - ('type', ctypes.c_ushort), - ) + _fields_ = ( + ('address_num', DWORD), + ('index', DWORD), + ('mask', DWORD), + ('broadcast_address', DWORD), + ('reassembly_size', DWORD), + ('unused', ctypes.c_ushort), + ('type', ctypes.c_ushort), + ) - @property - def address(self): - "The address in big-endian" - _ = struct.pack('L', self.address_num) - return struct.unpack('!L', _)[0] + @property + def address(self): + "The address in big-endian" + _ = struct.pack('L', self.address_num) + return struct.unpack('!L', _)[0] class MIB_IPADDRTABLE(ctypes.Structure): - _fields_ = ( - ('num_entries', DWORD), - ('entries', MIB_IPADDRROW * 0), - ) + _fields_ = (('num_entries', DWORD), ('entries', MIB_IPADDRROW * 0)) class SOCKADDR(ctypes.Structure): - _fields_ = ( - ('family', ctypes.c_ushort), - ('data', ctypes.c_byte * 14), - ) + _fields_ = (('family', ctypes.c_ushort), ('data', ctypes.c_byte * 14)) LPSOCKADDR = ctypes.POINTER(SOCKADDR) class SOCKET_ADDRESS(ctypes.Structure): - _fields_ = [ - ('address', LPSOCKADDR), - ('length', ctypes.c_int), - ] + _fields_ = [('address', LPSOCKADDR), ('length', ctypes.c_int)] class _IP_ADAPTER_ADDRESSES_METRIC(ctypes.Structure): - _fields_ = [ - ('length', ctypes.c_ulong), - ('interface_index', DWORD), - ] + _fields_ = [('length', ctypes.c_ulong), ('interface_index', DWORD)] class _IP_ADAPTER_ADDRESSES_U1(ctypes.Union): - _fields_ = [ - ('alignment', ctypes.c_ulonglong), - ('metric', _IP_ADAPTER_ADDRESSES_METRIC), - ] + _fields_ = [ + ('alignment', ctypes.c_ulonglong), + ('metric', _IP_ADAPTER_ADDRESSES_METRIC), + ] class IP_ADAPTER_ADDRESSES(ctypes.Structure): - pass + pass LP_IP_ADAPTER_ADDRESSES = ctypes.POINTER(IP_ADAPTER_ADDRESSES) @@ -149,69 +137,69 @@ NET_IF_CONNECTION_TYPE = ctypes.c_uint # enum TUNNEL_TYPE = ctypes.c_uint # enum IP_ADAPTER_ADDRESSES._fields_ = [ - # ('u', _IP_ADAPTER_ADDRESSES_U1), - ('length', ctypes.c_ulong), - ('interface_index', DWORD), - ('next', LP_IP_ADAPTER_ADDRESSES), - ('adapter_name', ctypes.c_char_p), - ('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS), - ('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS), - ('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS), - ('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS), - ('dns_suffix', ctypes.c_wchar_p), - ('description', ctypes.c_wchar_p), - ('friendly_name', ctypes.c_wchar_p), - ('byte', BYTE * MAX_ADAPTER_ADDRESS_LENGTH), - ('physical_address_length', DWORD), - ('flags', DWORD), - ('mtu', DWORD), - ('interface_type', DWORD), - ('oper_status', IF_OPER_STATUS), - ('ipv6_interface_index', DWORD), - ('zone_indices', DWORD), - ('first_prefix', PIP_ADAPTER_PREFIX), - ('transmit_link_speed', ctypes.c_uint64), - ('receive_link_speed', ctypes.c_uint64), - ('first_wins_server_address', PIP_ADAPTER_WINS_SERVER_ADDRESS_LH), - ('first_gateway_address', PIP_ADAPTER_GATEWAY_ADDRESS_LH), - ('ipv4_metric', ctypes.c_ulong), - ('ipv6_metric', ctypes.c_ulong), - ('luid', IF_LUID), - ('dhcpv4_server', SOCKET_ADDRESS), - ('compartment_id', NET_IF_COMPARTMENT_ID), - ('network_guid', NET_IF_NETWORK_GUID), - ('connection_type', NET_IF_CONNECTION_TYPE), - ('tunnel_type', TUNNEL_TYPE), - ('dhcpv6_server', SOCKET_ADDRESS), - ('dhcpv6_client_duid', ctypes.c_byte * MAX_DHCPV6_DUID_LENGTH), - ('dhcpv6_client_duid_length', ctypes.c_ulong), - ('dhcpv6_iaid', ctypes.c_ulong), - ('first_dns_suffix', PIP_ADAPTER_DNS_SUFFIX), + # ('u', _IP_ADAPTER_ADDRESSES_U1), + ('length', ctypes.c_ulong), + ('interface_index', DWORD), + ('next', LP_IP_ADAPTER_ADDRESSES), + ('adapter_name', ctypes.c_char_p), + ('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS), + ('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS), + ('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS), + ('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS), + ('dns_suffix', ctypes.c_wchar_p), + ('description', ctypes.c_wchar_p), + ('friendly_name', ctypes.c_wchar_p), + ('byte', BYTE * MAX_ADAPTER_ADDRESS_LENGTH), + ('physical_address_length', DWORD), + ('flags', DWORD), + ('mtu', DWORD), + ('interface_type', DWORD), + ('oper_status', IF_OPER_STATUS), + ('ipv6_interface_index', DWORD), + ('zone_indices', DWORD), + ('first_prefix', PIP_ADAPTER_PREFIX), + ('transmit_link_speed', ctypes.c_uint64), + ('receive_link_speed', ctypes.c_uint64), + ('first_wins_server_address', PIP_ADAPTER_WINS_SERVER_ADDRESS_LH), + ('first_gateway_address', PIP_ADAPTER_GATEWAY_ADDRESS_LH), + ('ipv4_metric', ctypes.c_ulong), + ('ipv6_metric', ctypes.c_ulong), + ('luid', IF_LUID), + ('dhcpv4_server', SOCKET_ADDRESS), + ('compartment_id', NET_IF_COMPARTMENT_ID), + ('network_guid', NET_IF_NETWORK_GUID), + ('connection_type', NET_IF_CONNECTION_TYPE), + ('tunnel_type', TUNNEL_TYPE), + ('dhcpv6_server', SOCKET_ADDRESS), + ('dhcpv6_client_duid', ctypes.c_byte * MAX_DHCPV6_DUID_LENGTH), + ('dhcpv6_client_duid_length', ctypes.c_ulong), + ('dhcpv6_iaid', ctypes.c_ulong), + ('first_dns_suffix', PIP_ADAPTER_DNS_SUFFIX), ] # define some parameters to the API Functions GetIfTable = ctypes.windll.iphlpapi.GetIfTable GetIfTable.argtypes = [ - ctypes.POINTER(MIB_IFTABLE), - ctypes.POINTER(ctypes.c_ulong), - BOOL, + ctypes.POINTER(MIB_IFTABLE), + ctypes.POINTER(ctypes.c_ulong), + BOOL, ] GetIfTable.restype = DWORD GetIpAddrTable = ctypes.windll.iphlpapi.GetIpAddrTable GetIpAddrTable.argtypes = [ - ctypes.POINTER(MIB_IPADDRTABLE), - ctypes.POINTER(ctypes.c_ulong), - BOOL, + ctypes.POINTER(MIB_IPADDRTABLE), + ctypes.POINTER(ctypes.c_ulong), + BOOL, ] GetIpAddrTable.restype = DWORD GetAdaptersAddresses = ctypes.windll.iphlpapi.GetAdaptersAddresses GetAdaptersAddresses.argtypes = [ - ctypes.c_ulong, - ctypes.c_ulong, - ctypes.c_void_p, - ctypes.POINTER(IP_ADAPTER_ADDRESSES), - ctypes.POINTER(ctypes.c_ulong), + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_void_p, + ctypes.POINTER(IP_ADAPTER_ADDRESSES), + ctypes.POINTER(ctypes.c_ulong), ] GetAdaptersAddresses.restype = ctypes.c_ulong diff --git a/libs/win/jaraco/windows/api/library.py b/libs/win/jaraco/windows/api/library.py index 7f14a58e..736de57c 100644 --- a/libs/win/jaraco/windows/api/library.py +++ b/libs/win/jaraco/windows/api/library.py @@ -2,8 +2,8 @@ import ctypes.wintypes GetModuleFileName = ctypes.windll.kernel32.GetModuleFileNameW GetModuleFileName.argtypes = ( - ctypes.wintypes.HANDLE, - ctypes.wintypes.LPWSTR, - ctypes.wintypes.DWORD, + ctypes.wintypes.HANDLE, + ctypes.wintypes.LPWSTR, + ctypes.wintypes.DWORD, ) GetModuleFileName.restype = ctypes.wintypes.DWORD diff --git a/libs/win/jaraco/windows/api/memory.py b/libs/win/jaraco/windows/api/memory.py index 0371099c..c8b60472 100644 --- a/libs/win/jaraco/windows/api/memory.py +++ b/libs/win/jaraco/windows/api/memory.py @@ -7,25 +7,25 @@ GlobalAlloc.argtypes = ctypes.wintypes.UINT, ctypes.c_size_t GlobalAlloc.restype = ctypes.wintypes.HANDLE GlobalLock = ctypes.windll.kernel32.GlobalLock -GlobalLock.argtypes = ctypes.wintypes.HGLOBAL, +GlobalLock.argtypes = (ctypes.wintypes.HGLOBAL,) GlobalLock.restype = ctypes.wintypes.LPVOID GlobalUnlock = ctypes.windll.kernel32.GlobalUnlock -GlobalUnlock.argtypes = ctypes.wintypes.HGLOBAL, +GlobalUnlock.argtypes = (ctypes.wintypes.HGLOBAL,) GlobalUnlock.restype = ctypes.wintypes.BOOL GlobalSize = ctypes.windll.kernel32.GlobalSize -GlobalSize.argtypes = ctypes.wintypes.HGLOBAL, +GlobalSize.argtypes = (ctypes.wintypes.HGLOBAL,) GlobalSize.restype = ctypes.c_size_t CreateFileMapping = ctypes.windll.kernel32.CreateFileMappingW CreateFileMapping.argtypes = [ - ctypes.wintypes.HANDLE, - ctypes.c_void_p, - ctypes.wintypes.DWORD, - ctypes.wintypes.DWORD, - ctypes.wintypes.DWORD, - ctypes.wintypes.LPWSTR, + ctypes.wintypes.HANDLE, + ctypes.c_void_p, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ctypes.wintypes.LPWSTR, ] CreateFileMapping.restype = ctypes.wintypes.HANDLE @@ -33,13 +33,9 @@ MapViewOfFile = ctypes.windll.kernel32.MapViewOfFile MapViewOfFile.restype = ctypes.wintypes.HANDLE UnmapViewOfFile = ctypes.windll.kernel32.UnmapViewOfFile -UnmapViewOfFile.argtypes = ctypes.wintypes.HANDLE, +UnmapViewOfFile.argtypes = (ctypes.wintypes.HANDLE,) RtlMoveMemory = ctypes.windll.kernel32.RtlMoveMemory -RtlMoveMemory.argtypes = ( - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_size_t, -) +RtlMoveMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t) -ctypes.windll.kernel32.LocalFree.argtypes = ctypes.wintypes.HLOCAL, +ctypes.windll.kernel32.LocalFree.argtypes = (ctypes.wintypes.HLOCAL,) diff --git a/libs/win/jaraco/windows/api/message.py b/libs/win/jaraco/windows/api/message.py index 5ce2d808..c4c20bad 100644 --- a/libs/win/jaraco/windows/api/message.py +++ b/libs/win/jaraco/windows/api/message.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - """ jaraco.windows.message @@ -9,22 +7,21 @@ Windows Messaging support import ctypes from ctypes.wintypes import HWND, UINT, WPARAM, LPARAM, DWORD, LPVOID -import six - LRESULT = LPARAM class LPARAM_wstr(LPARAM): - """ - A special instance of LPARAM that can be constructed from a string - instance (for functions such as SendMessage, whose LPARAM may point to - a unicode string). - """ - @classmethod - def from_param(cls, param): - if isinstance(param, six.string_types): - return LPVOID.from_param(six.text_type(param)) - return LPARAM.from_param(param) + """ + A special instance of LPARAM that can be constructed from a string + instance (for functions such as SendMessage, whose LPARAM may point to + a unicode string). + """ + + @classmethod + def from_param(cls, param): + if isinstance(param, str): + return LPVOID.from_param(str(param)) + return LPARAM.from_param(param) SendMessage = ctypes.windll.user32.SendMessageW @@ -43,12 +40,10 @@ SMTO_NOTIMEOUTIFNOTHUNG = 0x08 SMTO_ERRORONEXIT = 0x20 SendMessageTimeout = ctypes.windll.user32.SendMessageTimeoutW -SendMessageTimeout.argtypes = SendMessage.argtypes + ( - UINT, UINT, ctypes.POINTER(DWORD) -) +SendMessageTimeout.argtypes = SendMessage.argtypes + (UINT, UINT, ctypes.POINTER(DWORD)) SendMessageTimeout.restype = LRESULT def unicode_as_lparam(source): - pointer = ctypes.cast(ctypes.c_wchar_p(source), ctypes.c_void_p) - return LPARAM(pointer.value) + pointer = ctypes.cast(ctypes.c_wchar_p(source), ctypes.c_void_p) + return LPARAM(pointer.value) diff --git a/libs/win/jaraco/windows/api/net.py b/libs/win/jaraco/windows/api/net.py index ce693319..14defac2 100644 --- a/libs/win/jaraco/windows/api/net.py +++ b/libs/win/jaraco/windows/api/net.py @@ -7,24 +7,24 @@ RESOURCETYPE_ANY = 0 class NETRESOURCE(ctypes.Structure): - _fields_ = [ - ('scope', ctypes.wintypes.DWORD), - ('type', ctypes.wintypes.DWORD), - ('display_type', ctypes.wintypes.DWORD), - ('usage', ctypes.wintypes.DWORD), - ('local_name', ctypes.wintypes.LPWSTR), - ('remote_name', ctypes.wintypes.LPWSTR), - ('comment', ctypes.wintypes.LPWSTR), - ('provider', ctypes.wintypes.LPWSTR), - ] + _fields_ = [ + ('scope', ctypes.wintypes.DWORD), + ('type', ctypes.wintypes.DWORD), + ('display_type', ctypes.wintypes.DWORD), + ('usage', ctypes.wintypes.DWORD), + ('local_name', ctypes.wintypes.LPWSTR), + ('remote_name', ctypes.wintypes.LPWSTR), + ('comment', ctypes.wintypes.LPWSTR), + ('provider', ctypes.wintypes.LPWSTR), + ] LPNETRESOURCE = ctypes.POINTER(NETRESOURCE) WNetAddConnection2 = mpr.WNetAddConnection2W WNetAddConnection2.argtypes = ( - LPNETRESOURCE, - ctypes.wintypes.LPCWSTR, - ctypes.wintypes.LPCWSTR, - ctypes.wintypes.DWORD, + LPNETRESOURCE, + ctypes.wintypes.LPCWSTR, + ctypes.wintypes.LPCWSTR, + ctypes.wintypes.DWORD, ) diff --git a/libs/win/jaraco/windows/api/power.py b/libs/win/jaraco/windows/api/power.py index 77253a8a..b43de876 100644 --- a/libs/win/jaraco/windows/api/power.py +++ b/libs/win/jaraco/windows/api/power.py @@ -2,24 +2,23 @@ import ctypes.wintypes class SYSTEM_POWER_STATUS(ctypes.Structure): - _fields_ = ( - ('ac_line_status', ctypes.wintypes.BYTE), - ('battery_flag', ctypes.wintypes.BYTE), - ('battery_life_percent', ctypes.wintypes.BYTE), - ('reserved', ctypes.wintypes.BYTE), - ('battery_life_time', ctypes.wintypes.DWORD), - ('battery_full_life_time', ctypes.wintypes.DWORD), - ) + _fields_ = ( + ('ac_line_status', ctypes.wintypes.BYTE), + ('battery_flag', ctypes.wintypes.BYTE), + ('battery_life_percent', ctypes.wintypes.BYTE), + ('reserved', ctypes.wintypes.BYTE), + ('battery_life_time', ctypes.wintypes.DWORD), + ('battery_full_life_time', ctypes.wintypes.DWORD), + ) - @property - def ac_line_status_string(self): - return { - 0: 'offline', 1: 'online', 255: 'unknown'}[self.ac_line_status] + @property + def ac_line_status_string(self): + return {0: 'offline', 1: 'online', 255: 'unknown'}[self.ac_line_status] LPSYSTEM_POWER_STATUS = ctypes.POINTER(SYSTEM_POWER_STATUS) GetSystemPowerStatus = ctypes.windll.kernel32.GetSystemPowerStatus -GetSystemPowerStatus.argtypes = LPSYSTEM_POWER_STATUS, +GetSystemPowerStatus.argtypes = (LPSYSTEM_POWER_STATUS,) GetSystemPowerStatus.restype = ctypes.wintypes.BOOL SetThreadExecutionState = ctypes.windll.kernel32.SetThreadExecutionState @@ -28,10 +27,11 @@ SetThreadExecutionState.restype = ctypes.c_uint class ES: - """ - Execution state constants - """ - continuous = 0x80000000 - system_required = 1 - display_required = 2 - awaymode_required = 0x40 + """ + Execution state constants + """ + + continuous = 0x80000000 + system_required = 1 + display_required = 2 + awaymode_required = 0x40 diff --git a/libs/win/jaraco/windows/api/privilege.py b/libs/win/jaraco/windows/api/privilege.py index b841311e..7fb6a497 100644 --- a/libs/win/jaraco/windows/api/privilege.py +++ b/libs/win/jaraco/windows/api/privilege.py @@ -2,35 +2,32 @@ import ctypes.wintypes class LUID(ctypes.Structure): - _fields_ = [ - ('low_part', ctypes.wintypes.DWORD), - ('high_part', ctypes.wintypes.LONG), - ] + _fields_ = [ + ('low_part', ctypes.wintypes.DWORD), + ('high_part', ctypes.wintypes.LONG), + ] - def __eq__(self, other): - return ( - self.high_part == other.high_part and - self.low_part == other.low_part - ) + def __eq__(self, other): + return self.high_part == other.high_part and self.low_part == other.low_part - def __ne__(self, other): - return not (self == other) + def __ne__(self, other): + return not (self == other) LookupPrivilegeValue = ctypes.windll.advapi32.LookupPrivilegeValueW LookupPrivilegeValue.argtypes = ( - ctypes.wintypes.LPWSTR, # system name - ctypes.wintypes.LPWSTR, # name - ctypes.POINTER(LUID), + ctypes.wintypes.LPWSTR, # system name + ctypes.wintypes.LPWSTR, # name + ctypes.POINTER(LUID), ) LookupPrivilegeValue.restype = ctypes.wintypes.BOOL class TOKEN_INFORMATION_CLASS: - TokenUser = 1 - TokenGroups = 2 - TokenPrivileges = 3 - # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx + TokenUser = 1 + TokenGroups = 2 + TokenPrivileges = 3 + # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001 @@ -40,67 +37,63 @@ SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000 class LUID_AND_ATTRIBUTES(ctypes.Structure): - _fields_ = [ - ('LUID', LUID), - ('attributes', ctypes.wintypes.DWORD), - ] + _fields_ = [('LUID', LUID), ('attributes', ctypes.wintypes.DWORD)] - def is_enabled(self): - return bool(self.attributes & SE_PRIVILEGE_ENABLED) + def is_enabled(self): + return bool(self.attributes & SE_PRIVILEGE_ENABLED) - def enable(self): - self.attributes |= SE_PRIVILEGE_ENABLED + def enable(self): + self.attributes |= SE_PRIVILEGE_ENABLED - def get_name(self): - size = ctypes.wintypes.DWORD(10240) - buf = ctypes.create_unicode_buffer(size.value) - res = LookupPrivilegeName(None, self.LUID, buf, size) - if res == 0: - raise RuntimeError - return buf[:size.value] + def get_name(self): + size = ctypes.wintypes.DWORD(10240) + buf = ctypes.create_unicode_buffer(size.value) + res = LookupPrivilegeName(None, self.LUID, buf, size) + if res == 0: + raise RuntimeError + return buf[: size.value] - def __str__(self): - res = self.get_name() - if self.is_enabled(): - res += ' (enabled)' - return res + def __str__(self): + res = self.get_name() + if self.is_enabled(): + res += ' (enabled)' + return res LookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameW LookupPrivilegeName.argtypes = ( - ctypes.wintypes.LPWSTR, # lpSystemName - ctypes.POINTER(LUID), # lpLuid - ctypes.wintypes.LPWSTR, # lpName - ctypes.POINTER(ctypes.wintypes.DWORD), # cchName + ctypes.wintypes.LPWSTR, # lpSystemName + ctypes.POINTER(LUID), # lpLuid + ctypes.wintypes.LPWSTR, # lpName + ctypes.POINTER(ctypes.wintypes.DWORD), # cchName ) LookupPrivilegeName.restype = ctypes.wintypes.BOOL class TOKEN_PRIVILEGES(ctypes.Structure): - _fields_ = [ - ('count', ctypes.wintypes.DWORD), - ('privileges', LUID_AND_ATTRIBUTES * 0), - ] + _fields_ = [ + ('count', ctypes.wintypes.DWORD), + ('privileges', LUID_AND_ATTRIBUTES * 0), + ] - def get_array(self): - array_type = LUID_AND_ATTRIBUTES * self.count - privileges = ctypes.cast( - self.privileges, ctypes.POINTER(array_type)).contents - return privileges + def get_array(self): + array_type = LUID_AND_ATTRIBUTES * self.count + privileges = ctypes.cast(self.privileges, ctypes.POINTER(array_type)).contents + return privileges - def __iter__(self): - return iter(self.get_array()) + def __iter__(self): + return iter(self.get_array()) PTOKEN_PRIVILEGES = ctypes.POINTER(TOKEN_PRIVILEGES) GetTokenInformation = ctypes.windll.advapi32.GetTokenInformation GetTokenInformation.argtypes = [ - ctypes.wintypes.HANDLE, # TokenHandle - ctypes.c_uint, # TOKEN_INFORMATION_CLASS value - ctypes.c_void_p, # TokenInformation - ctypes.wintypes.DWORD, # TokenInformationLength - ctypes.POINTER(ctypes.wintypes.DWORD), # ReturnLength + ctypes.wintypes.HANDLE, # TokenHandle + ctypes.c_uint, # TOKEN_INFORMATION_CLASS value + ctypes.c_void_p, # TokenInformation + ctypes.wintypes.DWORD, # TokenInformationLength + ctypes.POINTER(ctypes.wintypes.DWORD), # ReturnLength ] GetTokenInformation.restype = ctypes.wintypes.BOOL @@ -108,10 +101,10 @@ GetTokenInformation.restype = ctypes.wintypes.BOOL AdjustTokenPrivileges = ctypes.windll.advapi32.AdjustTokenPrivileges AdjustTokenPrivileges.restype = ctypes.wintypes.BOOL AdjustTokenPrivileges.argtypes = [ - ctypes.wintypes.HANDLE, # TokenHandle - ctypes.wintypes.BOOL, # DisableAllPrivileges - PTOKEN_PRIVILEGES, # NewState (optional) - ctypes.wintypes.DWORD, # BufferLength of PreviousState - PTOKEN_PRIVILEGES, # PreviousState (out, optional) - ctypes.POINTER(ctypes.wintypes.DWORD), # ReturnLength + ctypes.wintypes.HANDLE, # TokenHandle + ctypes.wintypes.BOOL, # DisableAllPrivileges + PTOKEN_PRIVILEGES, # NewState (optional) + ctypes.wintypes.DWORD, # BufferLength of PreviousState + PTOKEN_PRIVILEGES, # PreviousState (out, optional) + ctypes.POINTER(ctypes.wintypes.DWORD), # ReturnLength ] diff --git a/libs/win/jaraco/windows/api/process.py b/libs/win/jaraco/windows/api/process.py index 56ce7852..3337cba5 100644 --- a/libs/win/jaraco/windows/api/process.py +++ b/libs/win/jaraco/windows/api/process.py @@ -1,11 +1,13 @@ import ctypes.wintypes -TOKEN_ALL_ACCESS = 0xf01ff +TOKEN_ALL_ACCESS = 0xF01FF GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess GetCurrentProcess.restype = ctypes.wintypes.HANDLE OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken OpenProcessToken.argtypes = ( - ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, - ctypes.POINTER(ctypes.wintypes.HANDLE)) + ctypes.wintypes.HANDLE, + ctypes.wintypes.DWORD, + ctypes.POINTER(ctypes.wintypes.HANDLE), +) OpenProcessToken.restype = ctypes.wintypes.BOOL diff --git a/libs/win/jaraco/windows/api/security.py b/libs/win/jaraco/windows/api/security.py index c9e7b58e..db5d220c 100644 --- a/libs/win/jaraco/windows/api/security.py +++ b/libs/win/jaraco/windows/api/security.py @@ -24,116 +24,117 @@ POLICY_LOOKUP_NAMES = 0x00000800 POLICY_NOTIFICATION = 0x00001000 POLICY_ALL_ACCESS = ( - STANDARD_RIGHTS_REQUIRED | - POLICY_VIEW_LOCAL_INFORMATION | - POLICY_VIEW_AUDIT_INFORMATION | - POLICY_GET_PRIVATE_INFORMATION | - POLICY_TRUST_ADMIN | - POLICY_CREATE_ACCOUNT | - POLICY_CREATE_SECRET | - POLICY_CREATE_PRIVILEGE | - POLICY_SET_DEFAULT_QUOTA_LIMITS | - POLICY_SET_AUDIT_REQUIREMENTS | - POLICY_AUDIT_LOG_ADMIN | - POLICY_SERVER_ADMIN | - POLICY_LOOKUP_NAMES) + STANDARD_RIGHTS_REQUIRED + | POLICY_VIEW_LOCAL_INFORMATION + | POLICY_VIEW_AUDIT_INFORMATION + | POLICY_GET_PRIVATE_INFORMATION + | POLICY_TRUST_ADMIN + | POLICY_CREATE_ACCOUNT + | POLICY_CREATE_SECRET + | POLICY_CREATE_PRIVILEGE + | POLICY_SET_DEFAULT_QUOTA_LIMITS + | POLICY_SET_AUDIT_REQUIREMENTS + | POLICY_AUDIT_LOG_ADMIN + | POLICY_SERVER_ADMIN + | POLICY_LOOKUP_NAMES +) POLICY_READ = ( - STANDARD_RIGHTS_READ | - POLICY_VIEW_AUDIT_INFORMATION | - POLICY_GET_PRIVATE_INFORMATION) + STANDARD_RIGHTS_READ + | POLICY_VIEW_AUDIT_INFORMATION + | POLICY_GET_PRIVATE_INFORMATION +) POLICY_WRITE = ( - STANDARD_RIGHTS_WRITE | - POLICY_TRUST_ADMIN | - POLICY_CREATE_ACCOUNT | - POLICY_CREATE_SECRET | - POLICY_CREATE_PRIVILEGE | - POLICY_SET_DEFAULT_QUOTA_LIMITS | - POLICY_SET_AUDIT_REQUIREMENTS | - POLICY_AUDIT_LOG_ADMIN | - POLICY_SERVER_ADMIN) + STANDARD_RIGHTS_WRITE + | POLICY_TRUST_ADMIN + | POLICY_CREATE_ACCOUNT + | POLICY_CREATE_SECRET + | POLICY_CREATE_PRIVILEGE + | POLICY_SET_DEFAULT_QUOTA_LIMITS + | POLICY_SET_AUDIT_REQUIREMENTS + | POLICY_AUDIT_LOG_ADMIN + | POLICY_SERVER_ADMIN +) POLICY_EXECUTE = ( - STANDARD_RIGHTS_EXECUTE | - POLICY_VIEW_LOCAL_INFORMATION | - POLICY_LOOKUP_NAMES) + STANDARD_RIGHTS_EXECUTE | POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES +) class TokenAccess: - TOKEN_QUERY = 0x8 + TOKEN_QUERY = 0x8 class TokenInformationClass: - TokenUser = 1 + TokenUser = 1 class TOKEN_USER(ctypes.Structure): - num = 1 - _fields_ = [ - ('SID', ctypes.c_void_p), - ('ATTRIBUTES', ctypes.wintypes.DWORD), - ] + num = 1 + _fields_ = [('SID', ctypes.c_void_p), ('ATTRIBUTES', ctypes.wintypes.DWORD)] class SECURITY_DESCRIPTOR(ctypes.Structure): - """ - typedef struct _SECURITY_DESCRIPTOR - { - UCHAR Revision; - UCHAR Sbz1; - SECURITY_DESCRIPTOR_CONTROL Control; - PSID Owner; - PSID Group; - PACL Sacl; - PACL Dacl; - } SECURITY_DESCRIPTOR; - """ - SECURITY_DESCRIPTOR_CONTROL = ctypes.wintypes.USHORT - REVISION = 1 + """ + typedef struct _SECURITY_DESCRIPTOR + { + UCHAR Revision; + UCHAR Sbz1; + SECURITY_DESCRIPTOR_CONTROL Control; + PSID Owner; + PSID Group; + PACL Sacl; + PACL Dacl; + } SECURITY_DESCRIPTOR; + """ - _fields_ = [ - ('Revision', ctypes.c_ubyte), - ('Sbz1', ctypes.c_ubyte), - ('Control', SECURITY_DESCRIPTOR_CONTROL), - ('Owner', ctypes.c_void_p), - ('Group', ctypes.c_void_p), - ('Sacl', ctypes.c_void_p), - ('Dacl', ctypes.c_void_p), - ] + SECURITY_DESCRIPTOR_CONTROL = ctypes.wintypes.USHORT + REVISION = 1 + + _fields_ = [ + ('Revision', ctypes.c_ubyte), + ('Sbz1', ctypes.c_ubyte), + ('Control', SECURITY_DESCRIPTOR_CONTROL), + ('Owner', ctypes.c_void_p), + ('Group', ctypes.c_void_p), + ('Sacl', ctypes.c_void_p), + ('Dacl', ctypes.c_void_p), + ] class SECURITY_ATTRIBUTES(ctypes.Structure): - """ - typedef struct _SECURITY_ATTRIBUTES { - DWORD nLength; - LPVOID lpSecurityDescriptor; - BOOL bInheritHandle; - } SECURITY_ATTRIBUTES; - """ - _fields_ = [ - ('nLength', ctypes.wintypes.DWORD), - ('lpSecurityDescriptor', ctypes.c_void_p), - ('bInheritHandle', ctypes.wintypes.BOOL), - ] + """ + typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; + } SECURITY_ATTRIBUTES; + """ - def __init__(self, *args, **kwargs): - super(SECURITY_ATTRIBUTES, self).__init__(*args, **kwargs) - self.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES) + _fields_ = [ + ('nLength', ctypes.wintypes.DWORD), + ('lpSecurityDescriptor', ctypes.c_void_p), + ('bInheritHandle', ctypes.wintypes.BOOL), + ] - @property - def descriptor(self): - return self._descriptor + def __init__(self, *args, **kwargs): + super(SECURITY_ATTRIBUTES, self).__init__(*args, **kwargs) + self.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES) - @descriptor.setter - def descriptor(self, value): - self._descriptor = value - self.lpSecurityDescriptor = ctypes.addressof(value) + @property + def descriptor(self): + return self._descriptor + + @descriptor.setter + def descriptor(self, value): + self._descriptor = value + self.lpSecurityDescriptor = ctypes.addressof(value) ctypes.windll.advapi32.SetSecurityDescriptorOwner.argtypes = ( - ctypes.POINTER(SECURITY_DESCRIPTOR), - ctypes.c_void_p, - ctypes.wintypes.BOOL, + ctypes.POINTER(SECURITY_DESCRIPTOR), + ctypes.c_void_p, + ctypes.wintypes.BOOL, ) diff --git a/libs/win/jaraco/windows/api/shell.py b/libs/win/jaraco/windows/api/shell.py index 1d428c87..af7174dc 100644 --- a/libs/win/jaraco/windows/api/shell.py +++ b/libs/win/jaraco/windows/api/shell.py @@ -1,39 +1,40 @@ import ctypes.wintypes + BOOL = ctypes.wintypes.BOOL class SHELLSTATE(ctypes.Structure): - _fields_ = [ - ('show_all_objects', BOOL, 1), - ('show_extensions', BOOL, 1), - ('no_confirm_recycle', BOOL, 1), - ('show_sys_files', BOOL, 1), - ('show_comp_color', BOOL, 1), - ('double_click_in_web_view', BOOL, 1), - ('desktop_HTML', BOOL, 1), - ('win95_classic', BOOL, 1), - ('dont_pretty_path', BOOL, 1), - ('show_attrib_col', BOOL, 1), - ('map_network_drive_button', BOOL, 1), - ('show_info_tip', BOOL, 1), - ('hide_icons', BOOL, 1), - ('web_view', BOOL, 1), - ('filter', BOOL, 1), - ('show_super_hidden', BOOL, 1), - ('no_net_crawling', BOOL, 1), - ('win95_unused', ctypes.wintypes.DWORD), - ('param_sort', ctypes.wintypes.LONG), - ('sort_direction', ctypes.c_int), - ('version', ctypes.wintypes.UINT), - ('not_used', ctypes.wintypes.UINT), - ('sep_process', BOOL, 1), - ('start_panel_on', BOOL, 1), - ('show_start_page', BOOL, 1), - ('auto_check_select', BOOL, 1), - ('icons_only', BOOL, 1), - ('show_type_overlay', BOOL, 1), - ('spare_flags', ctypes.wintypes.UINT, 13), - ] + _fields_ = [ + ('show_all_objects', BOOL, 1), + ('show_extensions', BOOL, 1), + ('no_confirm_recycle', BOOL, 1), + ('show_sys_files', BOOL, 1), + ('show_comp_color', BOOL, 1), + ('double_click_in_web_view', BOOL, 1), + ('desktop_HTML', BOOL, 1), + ('win95_classic', BOOL, 1), + ('dont_pretty_path', BOOL, 1), + ('show_attrib_col', BOOL, 1), + ('map_network_drive_button', BOOL, 1), + ('show_info_tip', BOOL, 1), + ('hide_icons', BOOL, 1), + ('web_view', BOOL, 1), + ('filter', BOOL, 1), + ('show_super_hidden', BOOL, 1), + ('no_net_crawling', BOOL, 1), + ('win95_unused', ctypes.wintypes.DWORD), + ('param_sort', ctypes.wintypes.LONG), + ('sort_direction', ctypes.c_int), + ('version', ctypes.wintypes.UINT), + ('not_used', ctypes.wintypes.UINT), + ('sep_process', BOOL, 1), + ('start_panel_on', BOOL, 1), + ('show_start_page', BOOL, 1), + ('auto_check_select', BOOL, 1), + ('icons_only', BOOL, 1), + ('show_type_overlay', BOOL, 1), + ('spare_flags', ctypes.wintypes.UINT, 13), + ] SSF_SHOWALLOBJECTS = 0x00000001 @@ -123,8 +124,8 @@ SSF_SHOWTYPEOVERLAY = 0x02000000 SHGetSetSettings = ctypes.windll.shell32.SHGetSetSettings SHGetSetSettings.argtypes = [ - ctypes.POINTER(SHELLSTATE), - ctypes.wintypes.DWORD, - ctypes.wintypes.BOOL, # get or set (True: set) + ctypes.POINTER(SHELLSTATE), + ctypes.wintypes.DWORD, + ctypes.wintypes.BOOL, # get or set (True: set) ] SHGetSetSettings.restype = None diff --git a/libs/win/jaraco/windows/api/system.py b/libs/win/jaraco/windows/api/system.py index 6a09f5ad..ba69affb 100644 --- a/libs/win/jaraco/windows/api/system.py +++ b/libs/win/jaraco/windows/api/system.py @@ -2,10 +2,10 @@ import ctypes.wintypes SystemParametersInfo = ctypes.windll.user32.SystemParametersInfoW SystemParametersInfo.argtypes = ( - ctypes.wintypes.UINT, - ctypes.wintypes.UINT, - ctypes.c_void_p, - ctypes.wintypes.UINT, + ctypes.wintypes.UINT, + ctypes.wintypes.UINT, + ctypes.c_void_p, + ctypes.wintypes.UINT, ) SPI_GETACTIVEWINDOWTRACKING = 0x1000 diff --git a/libs/win/jaraco/windows/api/user.py b/libs/win/jaraco/windows/api/user.py index 9d0b3f8d..936cdd23 100644 --- a/libs/win/jaraco/windows/api/user.py +++ b/libs/win/jaraco/windows/api/user.py @@ -1,9 +1,9 @@ import ctypes.wintypes try: - from ctypes.wintypes import LPDWORD + from ctypes.wintypes import LPDWORD except ImportError: - LPDWORD = ctypes.POINTER(ctypes.wintypes.DWORD) + LPDWORD = ctypes.POINTER(ctypes.wintypes.DWORD) # type: ignore GetUserName = ctypes.windll.advapi32.GetUserNameW GetUserName.argtypes = ctypes.wintypes.LPWSTR, LPDWORD diff --git a/libs/win/jaraco/windows/batch.py b/libs/win/jaraco/windows/batch.py new file mode 100644 index 00000000..b0ac6f0c --- /dev/null +++ b/libs/win/jaraco/windows/batch.py @@ -0,0 +1,39 @@ +import subprocess +import itertools + +from more_itertools import consume, always_iterable + + +def extract_environment(env_cmd, initial=None): + """ + Take a command (either a single command or list of arguments) + and return the environment created after running that command. + Note that if the command must be a batch file or .cmd file, or the + changes to the environment will not be captured. + + If initial is supplied, it is used as the initial environment passed + to the child process. + """ + # construct the command that will alter the environment + env_cmd = subprocess.list2cmdline(always_iterable(env_cmd)) + # create a tag so we can tell in the output when the proc is done + tag = 'Done running command' + # construct a cmd.exe command to do accomplish this + cmd = 'cmd.exe /s /c "{env_cmd} && echo "{tag}" && set"'.format(**vars()) + # launch the process + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=initial) + # parse the output sent to stdout + lines = proc.stdout + # make sure the lines are strings + + def make_str(s): + return s.decode() + + lines = map(make_str, lines) + # consume whatever output occurs until the tag is reached + consume(itertools.takewhile(lambda l: tag not in l, lines)) + # construct a dictionary of the pairs + result = dict(line.rstrip().split('=', 1) for line in lines) + # let the process finish + proc.communicate() + return result diff --git a/libs/win/jaraco/windows/clipboard.py b/libs/win/jaraco/windows/clipboard.py index 2f4bbc3a..cf7dc6bc 100644 --- a/libs/win/jaraco/windows/clipboard.py +++ b/libs/win/jaraco/windows/clipboard.py @@ -1,5 +1,3 @@ -from __future__ import with_statement, print_function - import sys import re import itertools @@ -7,220 +5,265 @@ from contextlib import contextmanager import io import ctypes from ctypes import windll - -import six -from six.moves import map +import textwrap +import collections from jaraco.windows.api import clipboard, memory from jaraco.windows.error import handle_nonzero_success, WindowsError from jaraco.windows.memory import LockedMemory -__all__ = ( - 'GetClipboardData', 'CloseClipboard', - 'SetClipboardData', 'OpenClipboard', -) +__all__ = ('GetClipboardData', 'CloseClipboard', 'SetClipboardData', 'OpenClipboard') def OpenClipboard(owner=None): - """ - Open the clipboard. + """ + Open the clipboard. - owner - [in] Handle to the window to be associated with the open clipboard. - If this parameter is None, the open clipboard is associated with the - current task. - """ - handle_nonzero_success(windll.user32.OpenClipboard(owner)) + owner + [in] Handle to the window to be associated with the open clipboard. + If this parameter is None, the open clipboard is associated with the + current task. + """ + handle_nonzero_success(windll.user32.OpenClipboard(owner)) def CloseClipboard(): - handle_nonzero_success(windll.user32.CloseClipboard()) + handle_nonzero_success(windll.user32.CloseClipboard()) data_handlers = dict() def handles(*formats): - def register(func): - for format in formats: - data_handlers[format] = func - return func - return register + def register(func): + for format in formats: + data_handlers[format] = func + return func + + return register def nts(buffer): - """ - Null Terminated String - Get the portion of bytestring buffer up to a null character. - """ - result, null, rest = buffer.partition('\x00') - return result + """ + Null Terminated String + Get the portion of bytestring buffer up to a null character. + """ + result, null, rest = buffer.partition('\x00') + return result @handles(clipboard.CF_DIBV5, clipboard.CF_DIB) def raw_data(handle): - return LockedMemory(handle).data + return LockedMemory(handle).data @handles(clipboard.CF_TEXT) def text_string(handle): - return nts(raw_data(handle)) + return nts(raw_data(handle)) @handles(clipboard.CF_UNICODETEXT) def unicode_string(handle): - return nts(raw_data(handle).decode('utf-16')) + return nts(raw_data(handle).decode('utf-16')) @handles(clipboard.CF_BITMAP) def as_bitmap(handle): - # handle is HBITMAP - raise NotImplementedError("Can't convert to DIB") - # todo: use GetDIBits http://msdn.microsoft.com - # /en-us/library/dd144879%28v=VS.85%29.aspx + # handle is HBITMAP + raise NotImplementedError("Can't convert to DIB") + # todo: use GetDIBits http://msdn.microsoft.com + # /en-us/library/dd144879%28v=VS.85%29.aspx @handles(clipboard.CF_HTML) class HTMLSnippet(object): - def __init__(self, handle): - self.data = nts(raw_data(handle).decode('utf-8')) - self.headers = self.parse_headers(self.data) + """ + HTML Snippet representing the Microsoft `HTML snippet format + `_. + """ - @property - def html(self): - return self.data[self.headers['StartHTML']:] + def __init__(self, handle): + self.data = nts(raw_data(handle).decode('utf-8')) + self.headers = self.parse_headers(self.data) - @staticmethod - def parse_headers(data): - d = io.StringIO(data) + @property + def html(self): + return self.data[self.headers['StartHTML'] :] - def header_line(line): - return re.match('(\w+):(.*)', line) - headers = map(header_line, d) - # grab headers until they no longer match - headers = itertools.takewhile(bool, headers) + @property + def fragment(self): + return self.data[self.headers['StartFragment'] : self.headers['EndFragment']] - def best_type(value): - try: - return int(value) - except ValueError: - pass - try: - return float(value) - except ValueError: - pass - return value - pairs = ( - (header.group(1), best_type(header.group(2))) - for header - in headers - ) - return dict(pairs) + @staticmethod + def parse_headers(data): + d = io.StringIO(data) + + def header_line(line): + return re.match(r'(\w+):(.*)', line) + + headers = map(header_line, d) + # grab headers until they no longer match + headers = itertools.takewhile(bool, headers) + + def best_type(value): + try: + return int(value) + except ValueError: + pass + try: + return float(value) + except ValueError: + pass + return value + + pairs = ((header.group(1), best_type(header.group(2))) for header in headers) + return dict(pairs) + + @classmethod + def from_string(cls, source): + """ + Construct an HTMLSnippet with all the headers, modeled after + https://docs.microsoft.com/en-us/troubleshoot/cpp/add-html-code-clipboard + """ + tmpl = textwrap.dedent( + """ + Version:0.9 + StartHTML:{start_html:08d} + EndHTML:{end_html:08d} + StartFragment:{start_fragment:08d} + EndFragment:{end_fragment:08d} + + + {source} + + + """ + ).strip() + zeros = collections.defaultdict(lambda: 0, locals()) + pre_value = tmpl.format_map(zeros) + start_html = pre_value.find('') + end_html = len(tmpl) + assert end_html < 100000000 + start_fragment = pre_value.find(source) + end_fragment = pre_value.rfind('\n %(target)s\n" % vars()) + """ + Like cmd.exe's mklink except it will infer directory status of the + target. + """ + from optparse import OptionParser + + parser = OptionParser(usage="usage: %prog [options] link target") + parser.add_option( + '-d', + '--directory', + help="Target is a directory (only necessary if not present)", + action="store_true", + ) + options, args = parser.parse_args() + try: + link, target = args + except ValueError: + parser.error("incorrect number of arguments") + symlink(target, link, options.directory) + sys.stdout.write("Symbolic link created: %(link)s --> %(target)s\n" % vars()) def _is_target_a_directory(link, rel_target): - """ - If creating a symlink from link to a target, determine if target - is a directory (relative to dirname(link)). - """ - target = os.path.join(os.path.dirname(link), rel_target) - return os.path.isdir(target) + """ + If creating a symlink from link to a target, determine if target + is a directory (relative to dirname(link)). + """ + target = os.path.join(os.path.dirname(link), rel_target) + return os.path.isdir(target) def symlink(target, link, target_is_directory=False): - """ - An implementation of os.symlink for Windows (Vista and greater) - """ - target_is_directory = ( - target_is_directory or - _is_target_a_directory(link, target) - ) - # normalize the target (MS symlinks don't respect forward slashes) - target = os.path.normpath(target) - handle_nonzero_success( - api.CreateSymbolicLink(link, target, target_is_directory)) + """ + An implementation of os.symlink for Windows (Vista and greater) + """ + target_is_directory = target_is_directory or _is_target_a_directory(link, target) + # normalize the target (MS symlinks don't respect forward slashes) + target = os.path.normpath(target) + flags = target_is_directory | api.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + handle_nonzero_success(api.CreateSymbolicLink(link, target, flags)) def link(target, link): - """ - Establishes a hard link between an existing file and a new file. - """ - handle_nonzero_success(api.CreateHardLink(link, target, None)) + """ + Establishes a hard link between an existing file and a new file. + """ + handle_nonzero_success(api.CreateHardLink(link, target, None)) def is_reparse_point(path): - """ - Determine if the given path is a reparse point. - Return False if the file does not exist or the file attributes cannot - be determined. - """ - res = api.GetFileAttributes(path) - return ( - res != api.INVALID_FILE_ATTRIBUTES - and bool(res & api.FILE_ATTRIBUTE_REPARSE_POINT) - ) + """ + Determine if the given path is a reparse point. + Return False if the file does not exist or the file attributes cannot + be determined. + """ + res = api.GetFileAttributes(path) + return res != api.INVALID_FILE_ATTRIBUTES and bool( + res & api.FILE_ATTRIBUTE_REPARSE_POINT + ) def islink(path): - "Determine if the given path is a symlink" - return is_reparse_point(path) and is_symlink(path) + "Determine if the given path is a symlink" + return is_reparse_point(path) and is_symlink(path) def _patch_path(path): - """ - Paths have a max length of api.MAX_PATH characters (260). If a target path - is longer than that, it needs to be made absolute and prepended with - \\?\ in order to work with API calls. - See http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx for - details. - """ - if path.startswith('\\\\?\\'): - return path - abs_path = os.path.abspath(path) - if not abs_path[1] == ':': - # python doesn't include the drive letter, but \\?\ requires it - abs_path = os.getcwd()[:2] + abs_path - return '\\\\?\\' + abs_path + r""" + Paths have a max length of api.MAX_PATH characters (260). If a target path + is longer than that, it needs to be made absolute and prepended with + \\?\ in order to work with API calls. + See http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx for + details. + """ + if path.startswith('\\\\?\\'): + return path + abs_path = os.path.abspath(path) + if not abs_path[1] == ':': + # python doesn't include the drive letter, but \\?\ requires it + abs_path = os.getcwd()[:2] + abs_path + return '\\\\?\\' + abs_path def is_symlink(path): - """ - Assuming path is a reparse point, determine if it's a symlink. - """ - path = _patch_path(path) - try: - return _is_symlink(next(find_files(path))) - except WindowsError as orig_error: - tmpl = "Error accessing {path}: {orig_error.message}" - raise builtins.WindowsError(tmpl.format(**locals())) + """ + Assuming path is a reparse point, determine if it's a symlink. + """ + path = _patch_path(path) + try: + return _is_symlink(next(find_files(path))) + # comment below workaround for PyCQA/pyflakes#376 + except WindowsError as orig_error: # noqa: F841 + tmpl = "Error accessing {path}: {orig_error.message}" + raise builtins.WindowsError(tmpl.format(**locals())) def _is_symlink(find_data): - return find_data.reserved[0] == api.IO_REPARSE_TAG_SYMLINK + return find_data.reserved[0] == api.IO_REPARSE_TAG_SYMLINK def find_files(spec): - """ - A pythonic wrapper around the FindFirstFile/FindNextFile win32 api. + r""" + A pythonic wrapper around the FindFirstFile/FindNextFile win32 api. - >>> root_files = tuple(find_files(r'c:\*')) - >>> len(root_files) > 1 - True - >>> root_files[0].filename == root_files[1].filename - False + >>> root_files = tuple(find_files(r'c:\*')) + >>> len(root_files) > 1 + True + >>> root_files[0].filename == root_files[1].filename + False - This test might fail on a non-standard installation - >>> 'Windows' in (fd.filename for fd in root_files) - True - """ - fd = api.WIN32_FIND_DATA() - handle = api.FindFirstFile(spec, byref(fd)) - while True: - if handle == api.INVALID_HANDLE_VALUE: - raise WindowsError() - yield fd - fd = api.WIN32_FIND_DATA() - res = api.FindNextFile(handle, byref(fd)) - if res == 0: # error - error = WindowsError() - if error.code == api.ERROR_NO_MORE_FILES: - break - else: - raise error - # todo: how to close handle when generator is destroyed? - # hint: catch GeneratorExit - windll.kernel32.FindClose(handle) + This test might fail on a non-standard installation + >>> 'Windows' in (fd.filename for fd in root_files) + True + """ + fd = api.WIN32_FIND_DATA() + handle = api.FindFirstFile(spec, byref(fd)) + while True: + if handle == api.INVALID_HANDLE_VALUE: + raise WindowsError() + yield fd + fd = api.WIN32_FIND_DATA() + res = api.FindNextFile(handle, byref(fd)) + if res == 0: # error + error = WindowsError() + if error.code == api.ERROR_NO_MORE_FILES: + break + else: + raise error + # todo: how to close handle when generator is destroyed? + # hint: catch GeneratorExit + windll.kernel32.FindClose(handle) def get_final_path(path): - """ - For a given path, determine the ultimate location of that path. - Useful for resolving symlink targets. - This functions wraps the GetFinalPathNameByHandle from the Windows - SDK. + r""" + For a given path, determine the ultimate location of that path. + Useful for resolving symlink targets. + This functions wraps the GetFinalPathNameByHandle from the Windows + SDK. - Note, this function fails if a handle cannot be obtained (such as - for C:\Pagefile.sys on a stock windows system). Consider using - trace_symlink_target instead. - """ - desired_access = api.NULL - share_mode = ( - api.FILE_SHARE_READ | api.FILE_SHARE_WRITE | api.FILE_SHARE_DELETE - ) - security_attributes = api.LPSECURITY_ATTRIBUTES() # NULL pointer - hFile = api.CreateFile( - path, - desired_access, - share_mode, - security_attributes, - api.OPEN_EXISTING, - api.FILE_FLAG_BACKUP_SEMANTICS, - api.NULL, - ) + Note, this function fails if a handle cannot be obtained (such as + for C:\Pagefile.sys on a stock windows system). Consider using + trace_symlink_target instead. + """ + desired_access = api.NULL + share_mode = api.FILE_SHARE_READ | api.FILE_SHARE_WRITE | api.FILE_SHARE_DELETE + security_attributes = api.LPSECURITY_ATTRIBUTES() # NULL pointer + hFile = api.CreateFile( + path, + desired_access, + share_mode, + security_attributes, + api.OPEN_EXISTING, + api.FILE_FLAG_BACKUP_SEMANTICS, + api.NULL, + ) - if hFile == api.INVALID_HANDLE_VALUE: - raise WindowsError() + if hFile == api.INVALID_HANDLE_VALUE: + raise WindowsError() - buf_size = api.GetFinalPathNameByHandle( - hFile, LPWSTR(), 0, api.VOLUME_NAME_DOS) - handle_nonzero_success(buf_size) - buf = create_unicode_buffer(buf_size) - result_length = api.GetFinalPathNameByHandle( - hFile, buf, len(buf), api.VOLUME_NAME_DOS) + buf_size = api.GetFinalPathNameByHandle(hFile, LPWSTR(), 0, api.VOLUME_NAME_DOS) + handle_nonzero_success(buf_size) + buf = create_unicode_buffer(buf_size) + result_length = api.GetFinalPathNameByHandle( + hFile, buf, len(buf), api.VOLUME_NAME_DOS + ) - assert result_length < len(buf) - handle_nonzero_success(result_length) - handle_nonzero_success(api.CloseHandle(hFile)) + assert result_length < len(buf) + handle_nonzero_success(result_length) + handle_nonzero_success(api.CloseHandle(hFile)) - return buf[:result_length] + return buf[:result_length] def compat_stat(path): - """ - Generate stat as found on Python 3.2 and later. - """ - stat = os.stat(path) - info = get_file_info(path) - # rewrite st_ino, st_dev, and st_nlink based on file info - return nt.stat_result( - (stat.st_mode,) + - (info.file_index, info.volume_serial_number, info.number_of_links) + - stat[4:] - ) + """ + Generate stat as found on Python 3.2 and later. + """ + stat = os.stat(path) + info = get_file_info(path) + # rewrite st_ino, st_dev, and st_nlink based on file info + return nt.stat_result( + (stat.st_mode,) + + (info.file_index, info.volume_serial_number, info.number_of_links) + + stat[4:] + ) def samefile(f1, f2): - """ - Backport of samefile from Python 3.2 with support for Windows. - """ - return posixpath.samestat(compat_stat(f1), compat_stat(f2)) + """ + Backport of samefile from Python 3.2 with support for Windows. + """ + return posixpath.samestat(compat_stat(f1), compat_stat(f2)) def get_file_info(path): - # open the file the same way CPython does in posixmodule.c - desired_access = api.FILE_READ_ATTRIBUTES - share_mode = 0 - security_attributes = None - creation_disposition = api.OPEN_EXISTING - flags_and_attributes = ( - api.FILE_ATTRIBUTE_NORMAL | - api.FILE_FLAG_BACKUP_SEMANTICS | - api.FILE_FLAG_OPEN_REPARSE_POINT - ) - template_file = None + # open the file the same way CPython does in posixmodule.c + desired_access = api.FILE_READ_ATTRIBUTES + share_mode = 0 + security_attributes = None + creation_disposition = api.OPEN_EXISTING + flags_and_attributes = ( + api.FILE_ATTRIBUTE_NORMAL + | api.FILE_FLAG_BACKUP_SEMANTICS + | api.FILE_FLAG_OPEN_REPARSE_POINT + ) + template_file = None - handle = api.CreateFile( - path, - desired_access, - share_mode, - security_attributes, - creation_disposition, - flags_and_attributes, - template_file, - ) + handle = api.CreateFile( + path, + desired_access, + share_mode, + security_attributes, + creation_disposition, + flags_and_attributes, + template_file, + ) - if handle == api.INVALID_HANDLE_VALUE: - raise WindowsError() + if handle == api.INVALID_HANDLE_VALUE: + raise WindowsError() - info = api.BY_HANDLE_FILE_INFORMATION() - res = api.GetFileInformationByHandle(handle, info) - handle_nonzero_success(res) - handle_nonzero_success(api.CloseHandle(handle)) + info = api.BY_HANDLE_FILE_INFORMATION() + res = api.GetFileInformationByHandle(handle, info) + handle_nonzero_success(res) + handle_nonzero_success(api.CloseHandle(handle)) - return info + return info def GetBinaryType(filepath): - res = api.DWORD() - handle_nonzero_success(api._GetBinaryType(filepath, res)) - return res + res = api.DWORD() + handle_nonzero_success(api._GetBinaryType(filepath, res)) + return res def _make_null_terminated_list(obs): - obs = _makelist(obs) - if obs is None: - return - return u'\x00'.join(obs) + u'\x00\x00' + obs = _makelist(obs) + if obs is None: + return + return u'\x00'.join(obs) + u'\x00\x00' def _makelist(ob): - if ob is None: - return - if not isinstance(ob, (list, tuple, set)): - return [ob] - return ob + if ob is None: + return + if not isinstance(ob, (list, tuple, set)): + return [ob] + return ob def SHFileOperation(operation, from_, to=None, flags=[]): - flags = functools.reduce(operator.or_, flags, 0) - from_ = _make_null_terminated_list(from_) - to = _make_null_terminated_list(to) - params = api.SHFILEOPSTRUCT(0, operation, from_, to, flags) - res = api._SHFileOperation(params) - if res != 0: - raise RuntimeError("SHFileOperation returned %d" % res) + flags = functools.reduce(operator.or_, flags, 0) + from_ = _make_null_terminated_list(from_) + to = _make_null_terminated_list(to) + params = api.SHFILEOPSTRUCT(0, operation, from_, to, flags) + res = api._SHFileOperation(params) + if res != 0: + raise RuntimeError("SHFileOperation returned %d" % res) def join(*paths): - r""" - Wrapper around os.path.join that works with Windows drive letters. + r""" + Wrapper around os.path.join that works with Windows drive letters. - >>> join('d:\\foo', '\\bar') - 'd:\\bar' - """ - paths_with_drives = map(os.path.splitdrive, paths) - drives, paths = zip(*paths_with_drives) - # the drive we care about is the last one in the list - drive = next(filter(None, reversed(drives)), '') - return os.path.join(drive, os.path.join(*paths)) + >>> join('d:\\foo', '\\bar') + 'd:\\bar' + """ + paths_with_drives = map(os.path.splitdrive, paths) + drives, paths = zip(*paths_with_drives) + # the drive we care about is the last one in the list + drive = next(filter(None, reversed(drives)), '') + return os.path.join(drive, os.path.join(*paths)) def resolve_path(target, start=os.path.curdir): - r""" - Find a path from start to target where target is relative to start. + r""" + Find a path from start to target where target is relative to start. - >>> tmp = str(getfixture('tmpdir_as_cwd')) + >>> tmp = str(getfixture('tmpdir_as_cwd')) - >>> findpath('d:\\') - 'd:\\' + >>> findpath('d:\\') + 'd:\\' - >>> findpath('d:\\', tmp) - 'd:\\' + >>> findpath('d:\\', tmp) + 'd:\\' - >>> findpath('\\bar', 'd:\\') - 'd:\\bar' + >>> findpath('\\bar', 'd:\\') + 'd:\\bar' - >>> findpath('\\bar', 'd:\\foo') # fails with '\\bar' - 'd:\\bar' + >>> findpath('\\bar', 'd:\\foo') # fails with '\\bar' + 'd:\\bar' - >>> findpath('bar', 'd:\\foo') - 'd:\\foo\\bar' + >>> findpath('bar', 'd:\\foo') + 'd:\\foo\\bar' - >>> findpath('\\baz', 'd:\\foo\\bar') # fails with '\\baz' - 'd:\\baz' + >>> findpath('\\baz', 'd:\\foo\\bar') # fails with '\\baz' + 'd:\\baz' - >>> os.path.abspath(findpath('\\bar')).lower() - 'c:\\bar' + >>> os.path.abspath(findpath('\\bar')).lower() + 'c:\\bar' - >>> os.path.abspath(findpath('bar')) - '...\\bar' + >>> os.path.abspath(findpath('bar')) + '...\\bar' - >>> findpath('..', 'd:\\foo\\bar') - 'd:\\foo' + >>> findpath('..', 'd:\\foo\\bar') + 'd:\\foo' - The parent of the root directory is the root directory. - >>> findpath('..', 'd:\\') - 'd:\\' - """ - return os.path.normpath(join(start, target)) + The parent of the root directory is the root directory. + >>> findpath('..', 'd:\\') + 'd:\\' + """ + return os.path.normpath(join(start, target)) findpath = resolve_path def trace_symlink_target(link): - """ - Given a file that is known to be a symlink, trace it to its ultimate - target. + """ + Given a file that is known to be a symlink, trace it to its ultimate + target. - Raises TargetNotPresent when the target cannot be determined. - Raises ValueError when the specified link is not a symlink. - """ + Raises TargetNotPresent when the target cannot be determined. + Raises ValueError when the specified link is not a symlink. + """ - if not is_symlink(link): - raise ValueError("link must point to a symlink on the system") - while is_symlink(link): - orig = os.path.dirname(link) - link = readlink(link) - link = resolve_path(link, orig) - return link + if not is_symlink(link): + raise ValueError("link must point to a symlink on the system") + while is_symlink(link): + orig = os.path.dirname(link) + link = readlink(link) + link = resolve_path(link, orig) + return link def readlink(link): - """ - readlink(link) -> target - Return a string representing the path to which the symbolic link points. - """ - handle = api.CreateFile( - link, - 0, - 0, - None, - api.OPEN_EXISTING, - api.FILE_FLAG_OPEN_REPARSE_POINT | api.FILE_FLAG_BACKUP_SEMANTICS, - None, - ) + """ + readlink(link) -> target + Return a string representing the path to which the symbolic link points. + """ + handle = api.CreateFile( + link, + 0, + 0, + None, + api.OPEN_EXISTING, + api.FILE_FLAG_OPEN_REPARSE_POINT | api.FILE_FLAG_BACKUP_SEMANTICS, + None, + ) - if handle == api.INVALID_HANDLE_VALUE: - raise WindowsError() + if handle == api.INVALID_HANDLE_VALUE: + raise WindowsError() - res = reparse.DeviceIoControl( - handle, api.FSCTL_GET_REPARSE_POINT, None, 10240) + res = reparse.DeviceIoControl(handle, api.FSCTL_GET_REPARSE_POINT, None, 10240) - bytes = create_string_buffer(res) - p_rdb = cast(bytes, POINTER(api.REPARSE_DATA_BUFFER)) - rdb = p_rdb.contents - if not rdb.tag == api.IO_REPARSE_TAG_SYMLINK: - raise RuntimeError("Expected IO_REPARSE_TAG_SYMLINK, but got %d" % rdb.tag) + bytes = create_string_buffer(res) + p_rdb = cast(bytes, POINTER(api.REPARSE_DATA_BUFFER)) + rdb = p_rdb.contents + if not rdb.tag == api.IO_REPARSE_TAG_SYMLINK: + raise RuntimeError("Expected IO_REPARSE_TAG_SYMLINK, but got %d" % rdb.tag) - handle_nonzero_success(api.CloseHandle(handle)) - return rdb.get_substitute_name() + handle_nonzero_success(api.CloseHandle(handle)) + return rdb.get_substitute_name() def patch_os_module(): - """ - jaraco.windows provides the os.symlink and os.readlink functions. - Monkey-patch the os module to include them if not present. - """ - if not hasattr(os, 'symlink'): - os.symlink = symlink - os.path.islink = islink - if not hasattr(os, 'readlink'): - os.readlink = readlink + """ + jaraco.windows provides the os.symlink and os.readlink functions. + Monkey-patch the os module to include them if not present. + """ + if not hasattr(os, 'symlink'): + os.symlink = symlink + os.path.islink = islink + if not hasattr(os, 'readlink'): + os.readlink = readlink def find_symlinks(root): - for dirpath, dirnames, filenames in os.walk(root): - for name in dirnames + filenames: - pathname = os.path.join(dirpath, name) - if is_symlink(pathname): - yield pathname - # don't traverse symlinks - if name in dirnames: - dirnames.remove(name) + for dirpath, dirnames, filenames in os.walk(root): + for name in dirnames + filenames: + pathname = os.path.join(dirpath, name) + if is_symlink(pathname): + yield pathname + # don't traverse symlinks + if name in dirnames: + dirnames.remove(name) def find_symlinks_cmd(): - """ - %prog [start-path] - Search the specified path (defaults to the current directory) for symlinks, - printing the source and target on each line. - """ - from optparse import OptionParser - from textwrap import dedent - parser = OptionParser(usage=dedent(find_symlinks_cmd.__doc__).strip()) - options, args = parser.parse_args() - if not args: - args = ['.'] - root = args.pop() - if args: - parser.error("unexpected argument(s)") - try: - for symlink in find_symlinks(root): - target = readlink(symlink) - dir = ['', 'D'][os.path.isdir(symlink)] - msg = '{dir:2}{symlink} --> {target}'.format(**locals()) - print(msg) - except KeyboardInterrupt: - pass + """ + %prog [start-path] + Search the specified path (defaults to the current directory) for symlinks, + printing the source and target on each line. + """ + from optparse import OptionParser + from textwrap import dedent + + parser = OptionParser(usage=dedent(find_symlinks_cmd.__doc__).strip()) + options, args = parser.parse_args() + if not args: + args = ['.'] + root = args.pop() + if args: + parser.error("unexpected argument(s)") + try: + for symlink in find_symlinks(root): + target = readlink(symlink) + dir = ['', 'D'][os.path.isdir(symlink)] + msg = '{dir:2}{symlink} --> {target}'.format(**locals()) + print(msg) + except KeyboardInterrupt: + pass -@six.add_metaclass(binary.BitMask) -class FileAttributes(int): +class FileAttributes(int, metaclass=binary.BitMask): - # extract the values from the stat module on Python 3.5 - # and later. - locals().update( - (name.split('FILE_ATTRIBUTES_')[1].lower(), value) - for name, value in vars(stat).items() - if name.startswith('FILE_ATTRIBUTES_') - ) + # extract the values from the stat module on Python 3.5 + # and later. + locals().update( + (name.split('FILE_ATTRIBUTES_')[1].lower(), value) + for name, value in vars(stat).items() + if name.startswith('FILE_ATTRIBUTES_') + ) - # For Python 3.4 and earlier, define the constants here - archive = 0x20 - compressed = 0x800 - hidden = 0x2 - device = 0x40 - directory = 0x10 - encrypted = 0x4000 - normal = 0x80 - not_content_indexed = 0x2000 - offline = 0x1000 - read_only = 0x1 - reparse_point = 0x400 - sparse_file = 0x200 - system = 0x4 - temporary = 0x100 - virtual = 0x10000 + # For Python 3.4 and earlier, define the constants here + archive = 0x20 + compressed = 0x800 + hidden = 0x2 + device = 0x40 + directory = 0x10 + encrypted = 0x4000 + normal = 0x80 + not_content_indexed = 0x2000 + offline = 0x1000 + read_only = 0x1 + reparse_point = 0x400 + sparse_file = 0x200 + system = 0x4 + temporary = 0x100 + virtual = 0x10000 - @classmethod - def get(cls, filepath): - attrs = api.GetFileAttributes(filepath) - if attrs == api.INVALID_FILE_ATTRIBUTES: - raise WindowsError() - return cls(attrs) + @classmethod + def get(cls, filepath): + attrs = api.GetFileAttributes(filepath) + if attrs == api.INVALID_FILE_ATTRIBUTES: + raise WindowsError() + return cls(attrs) GetFileAttributes = FileAttributes.get def SetFileAttributes(filepath, *attrs): - """ - Set file attributes. e.g.: + """ + Set file attributes. e.g.: - SetFileAttributes('C:\\foo', 'hidden') + SetFileAttributes('C:\\foo', 'hidden') - Each attr must be either a numeric value, a constant defined in - jaraco.windows.filesystem.api, or one of the nice names - defined in this function. - """ - nice_names = collections.defaultdict( - lambda key: key, - hidden='FILE_ATTRIBUTE_HIDDEN', - read_only='FILE_ATTRIBUTE_READONLY', - ) - flags = (getattr(api, nice_names[attr], attr) for attr in attrs) - flags = functools.reduce(operator.or_, flags) - handle_nonzero_success(api.SetFileAttributes(filepath, flags)) + Each attr must be either a numeric value, a constant defined in + jaraco.windows.filesystem.api, or one of the nice names + defined in this function. + """ + nice_names = collections.defaultdict( + lambda key: key, + hidden='FILE_ATTRIBUTE_HIDDEN', + read_only='FILE_ATTRIBUTE_READONLY', + ) + flags = (getattr(api, nice_names[attr], attr) for attr in attrs) + flags = functools.reduce(operator.or_, flags) + handle_nonzero_success(api.SetFileAttributes(filepath, flags)) diff --git a/libs/win/jaraco/windows/filesystem/backports.py b/libs/win/jaraco/windows/filesystem/backports.py index abb45d07..d26c92b8 100644 --- a/libs/win/jaraco/windows/filesystem/backports.py +++ b/libs/win/jaraco/windows/filesystem/backports.py @@ -1,109 +1,107 @@ -from __future__ import unicode_literals - import os.path # realpath taken from https://bugs.python.org/file38057/issue9949-v4.patch def realpath(path): - if isinstance(path, str): - prefix = '\\\\?\\' - unc_prefix = prefix + 'UNC' - new_unc_prefix = '\\' - cwd = os.getcwd() - else: - prefix = b'\\\\?\\' - unc_prefix = prefix + b'UNC' - new_unc_prefix = b'\\' - cwd = os.getcwdb() - had_prefix = path.startswith(prefix) - path, ok = _resolve_path(cwd, path, {}) - # The path returned by _getfinalpathname will always start with \\?\ - - # strip off that prefix unless it was already provided on the original - # path. - if not had_prefix: - # For UNC paths, the prefix will actually be \\?\UNC - handle that - # case as well. - if path.startswith(unc_prefix): - path = new_unc_prefix + path[len(unc_prefix):] - elif path.startswith(prefix): - path = path[len(prefix):] - return path + if isinstance(path, str): + prefix = '\\\\?\\' + unc_prefix = prefix + 'UNC' + new_unc_prefix = '\\' + cwd = os.getcwd() + else: + prefix = b'\\\\?\\' + unc_prefix = prefix + b'UNC' + new_unc_prefix = b'\\' + cwd = os.getcwdb() + had_prefix = path.startswith(prefix) + path, ok = _resolve_path(cwd, path, {}) + # The path returned by _getfinalpathname will always start with \\?\ - + # strip off that prefix unless it was already provided on the original + # path. + if not had_prefix: + # For UNC paths, the prefix will actually be \\?\UNC - handle that + # case as well. + if path.startswith(unc_prefix): + path = new_unc_prefix + path[len(unc_prefix) :] + elif path.startswith(prefix): + path = path[len(prefix) :] + return path -def _resolve_path(path, rest, seen): - # Windows normalizes the path before resolving symlinks; be sure to - # follow the same behavior. - rest = os.path.normpath(rest) +def _resolve_path(path, rest, seen): # noqa: C901 + # Windows normalizes the path before resolving symlinks; be sure to + # follow the same behavior. + rest = os.path.normpath(rest) - if isinstance(rest, str): - sep = '\\' - else: - sep = b'\\' + if isinstance(rest, str): + sep = '\\' + else: + sep = b'\\' - if os.path.isabs(rest): - drive, rest = os.path.splitdrive(rest) - path = drive + sep - rest = rest[1:] + if os.path.isabs(rest): + drive, rest = os.path.splitdrive(rest) + path = drive + sep + rest = rest[1:] - while rest: - name, _, rest = rest.partition(sep) - new_path = os.path.join(path, name) if path else name - if os.path.exists(new_path): - if not rest: - # The whole path exists. Resolve it using the OS. - path = os.path._getfinalpathname(new_path) - else: - # The OS can resolve `new_path`; keep traversing the path. - path = new_path - elif not os.path.lexists(new_path): - # `new_path` does not exist on the filesystem at all. Use the - # OS to resolve `path`, if it exists, and then append the - # remainder. - if os.path.exists(path): - path = os.path._getfinalpathname(path) - rest = os.path.join(name, rest) if rest else name - return os.path.join(path, rest), True - else: - # We have a symbolic link that the OS cannot resolve. Try to - # resolve it ourselves. + while rest: + name, _, rest = rest.partition(sep) + new_path = os.path.join(path, name) if path else name + if os.path.exists(new_path): + if not rest: + # The whole path exists. Resolve it using the OS. + path = os.path._getfinalpathname(new_path) + else: + # The OS can resolve `new_path`; keep traversing the path. + path = new_path + elif not os.path.lexists(new_path): + # `new_path` does not exist on the filesystem at all. Use the + # OS to resolve `path`, if it exists, and then append the + # remainder. + if os.path.exists(path): + path = os.path._getfinalpathname(path) + rest = os.path.join(name, rest) if rest else name + return os.path.join(path, rest), True + else: + # We have a symbolic link that the OS cannot resolve. Try to + # resolve it ourselves. - # On Windows, symbolic link resolution can be partially or - # fully disabled [1]. The end result of a disabled symlink - # appears the same as a broken symlink (lexists() returns True - # but exists() returns False). And in both cases, the link can - # still be read using readlink(). Call stat() and check the - # resulting error code to ensure we don't circumvent the - # Windows symbolic link restrictions. - # [1] https://technet.microsoft.com/en-us/library/cc754077.aspx - try: - os.stat(new_path) - except OSError as e: - # WinError 1463: The symbolic link cannot be followed - # because its type is disabled. - if e.winerror == 1463: - raise + # On Windows, symbolic link resolution can be partially or + # fully disabled [1]. The end result of a disabled symlink + # appears the same as a broken symlink (lexists() returns True + # but exists() returns False). And in both cases, the link can + # still be read using readlink(). Call stat() and check the + # resulting error code to ensure we don't circumvent the + # Windows symbolic link restrictions. + # [1] https://technet.microsoft.com/en-us/library/cc754077.aspx + try: + os.stat(new_path) + except OSError as e: + # WinError 1463: The symbolic link cannot be followed + # because its type is disabled. + if e.winerror == 1463: + raise - key = os.path.normcase(new_path) - if key in seen: - # This link has already been seen; try to use the - # previously resolved value. - path = seen[key] - if path is None: - # It has not yet been resolved, which means we must - # have a symbolic link loop. Return what we have - # resolved so far plus the remainder of the path (who - # cares about the Zen of Python?). - path = os.path.join(new_path, rest) if rest else new_path - return path, False - else: - # Mark this link as in the process of being resolved. - seen[key] = None - # Try to resolve it. - path, ok = _resolve_path(path, os.readlink(new_path), seen) - if ok: - # Resolution succeded; store the resolved value. - seen[key] = path - else: - # Resolution failed; punt. - return (os.path.join(path, rest) if rest else path), False - return path, True + key = os.path.normcase(new_path) + if key in seen: + # This link has already been seen; try to use the + # previously resolved value. + path = seen[key] + if path is None: + # It has not yet been resolved, which means we must + # have a symbolic link loop. Return what we have + # resolved so far plus the remainder of the path (who + # cares about the Zen of Python?). + path = os.path.join(new_path, rest) if rest else new_path + return path, False + else: + # Mark this link as in the process of being resolved. + seen[key] = None + # Try to resolve it. + path, ok = _resolve_path(path, os.readlink(new_path), seen) + if ok: + # Resolution succeded; store the resolved value. + seen[key] = path + else: + # Resolution failed; punt. + return (os.path.join(path, rest) if rest else path), False + return path, True diff --git a/libs/win/jaraco/windows/filesystem/change.py b/libs/win/jaraco/windows/filesystem/change.py index 620d9272..7a3c508f 100644 --- a/libs/win/jaraco/windows/filesystem/change.py +++ b/libs/win/jaraco/windows/filesystem/change.py @@ -1,14 +1,10 @@ -# -*- coding: UTF-8 -*- - """ FileChange - Classes and routines for monitoring the file system for changes. + Classes and routines for monitoring the file system for changes. Copyright © 2004, 2011, 2013 Jason R. Coombs """ -from __future__ import print_function - import os import sys import datetime @@ -17,8 +13,6 @@ from threading import Thread import itertools import logging -import six - from more_itertools.recipes import consume import jaraco.text @@ -29,243 +23,237 @@ log = logging.getLogger(__name__) class NotifierException(Exception): - pass + pass class FileFilter(object): - def set_root(self, root): - self.root = root + def set_root(self, root): + self.root = root - def _get_file_path(self, filename): - try: - filename = os.path.join(self.root, filename) - except AttributeError: - pass - return filename + def _get_file_path(self, filename): + try: + filename = os.path.join(self.root, filename) + except AttributeError: + pass + return filename class ModifiedTimeFilter(FileFilter): - """ - Returns true for each call where the modified time of the file is after - the cutoff time. - """ - def __init__(self, cutoff): - self.cutoff = cutoff + """ + Returns true for each call where the modified time of the file is after + the cutoff time. + """ - def __call__(self, file): - filepath = self._get_file_path(file) - last_mod = datetime.datetime.utcfromtimestamp( - os.stat(filepath).st_mtime) - log.debug('{filepath} last modified at {last_mod}.'.format(**vars())) - return last_mod > self.cutoff + def __init__(self, cutoff): + self.cutoff = cutoff + + def __call__(self, file): + filepath = self._get_file_path(file) + last_mod = datetime.datetime.utcfromtimestamp(os.stat(filepath).st_mtime) + log.debug('{filepath} last modified at {last_mod}.'.format(**vars())) + return last_mod > self.cutoff class PatternFilter(FileFilter): - """ - Filter that returns True for files that match pattern (a regular - expression). - """ - def __init__(self, pattern): - self.pattern = ( - re.compile(pattern) if isinstance(pattern, six.string_types) - else pattern - ) + """ + Filter that returns True for files that match pattern (a regular + expression). + """ - def __call__(self, file): - return bool(self.pattern.match(file, re.I)) + def __init__(self, pattern): + self.pattern = re.compile(pattern) if isinstance(pattern, str) else pattern + + def __call__(self, file): + return bool(self.pattern.match(file, re.I)) class GlobFilter(PatternFilter): - """ - Filter that returns True for files that match the pattern (a glob - expression. - """ - def __init__(self, expression): - super(GlobFilter, self).__init__( - self.convert_file_pattern(expression)) + """ + Filter that returns True for files that match the pattern (a glob + expression. + """ - @staticmethod - def convert_file_pattern(p): - r""" - converts a filename specification (such as c:\*.*) to an equivelent - regular expression - >>> GlobFilter.convert_file_pattern('/*') - '/.*' - """ - subs = (('\\', '\\\\'), ('.', '\\.'), ('*', '.*'), ('?', '.')) - return jaraco.text.multi_substitution(*subs)(p) + def __init__(self, expression): + super(GlobFilter, self).__init__(self.convert_file_pattern(expression)) + + @staticmethod + def convert_file_pattern(p): + r""" + converts a filename specification (such as c:\*.*) to an equivelent + regular expression + >>> GlobFilter.convert_file_pattern('/*') + '/.*' + """ + subs = (('\\', '\\\\'), ('.', '\\.'), ('*', '.*'), ('?', '.')) + return jaraco.text.multi_substitution(*subs)(p) class AggregateFilter(FileFilter): - """ - This file filter will aggregate the filters passed to it, and when called, - will return the results of each filter ANDed together. - """ - def __init__(self, *filters): - self.filters = filters + """ + This file filter will aggregate the filters passed to it, and when called, + will return the results of each filter ANDed together. + """ - def set_root(self, root): - consume(f.set_root(root) for f in self.filters) + def __init__(self, *filters): + self.filters = filters - def __call__(self, file): - return all(fil(file) for fil in self.filters) + def set_root(self, root): + consume(f.set_root(root) for f in self.filters) + + def __call__(self, file): + return all(fil(file) for fil in self.filters) class OncePerModFilter(FileFilter): - def __init__(self): - self.history = list() + def __init__(self): + self.history = list() - def __call__(self, file): - file = os.path.join(self.root, file) - key = file, os.stat(file).st_mtime - result = key not in self.history - self.history.append(key) - if len(self.history) > 100: - del self.history[-50:] - return result + def __call__(self, file): + file = os.path.join(self.root, file) + key = file, os.stat(file).st_mtime + result = key not in self.history + self.history.append(key) + if len(self.history) > 100: + del self.history[-50:] + return result def files_with_path(files, path): - return (os.path.join(path, file) for file in files) + return (os.path.join(path, file) for file in files) def get_file_paths(walk_result): - root, dirs, files = walk_result - return files_with_path(files, root) + root, dirs, files = walk_result + return files_with_path(files, root) class Notifier(object): - def __init__(self, root='.', filters=[]): - # assign the root, verify it exists - self.root = root - if not os.path.isdir(self.root): - raise NotifierException( - 'Root directory "%s" does not exist' % self.root) - self.filters = filters + def __init__(self, root='.', filters=[]): + # assign the root, verify it exists + self.root = root + if not os.path.isdir(self.root): + raise NotifierException('Root directory "%s" does not exist' % self.root) + self.filters = filters - self.watch_subtree = False - self.quit_event = event.CreateEvent(None, 0, 0, None) - self.opm_filter = OncePerModFilter() + self.watch_subtree = False + self.quit_event = event.CreateEvent(None, 0, 0, None) + self.opm_filter = OncePerModFilter() - def __del__(self): - try: - fs.FindCloseChangeNotification(self.hChange) - except Exception: - pass + def __del__(self): + try: + fs.FindCloseChangeNotification(self.hChange) + except Exception: + pass - def _get_change_handle(self): - # set up to monitor the directory tree specified - self.hChange = fs.FindFirstChangeNotification( - self.root, - self.watch_subtree, - fs.FILE_NOTIFY_CHANGE_LAST_WRITE, - ) + def _get_change_handle(self): + # set up to monitor the directory tree specified + self.hChange = fs.FindFirstChangeNotification( + self.root, self.watch_subtree, fs.FILE_NOTIFY_CHANGE_LAST_WRITE + ) - # make sure it worked; if not, bail - INVALID_HANDLE_VALUE = fs.INVALID_HANDLE_VALUE - if self.hChange == INVALID_HANDLE_VALUE: - raise NotifierException( - 'Could not set up directory change notification') + # make sure it worked; if not, bail + INVALID_HANDLE_VALUE = fs.INVALID_HANDLE_VALUE + if self.hChange == INVALID_HANDLE_VALUE: + raise NotifierException('Could not set up directory change notification') - @staticmethod - def _filtered_walk(path, file_filter): - """ - static method that calls os.walk, but filters out - anything that doesn't match the filter - """ - for root, dirs, files in os.walk(path): - log.debug('looking in %s', root) - log.debug('files is %s', files) - file_filter.set_root(root) - files = filter(file_filter, files) - log.debug('filtered files is %s', files) - yield (root, dirs, files) + @staticmethod + def _filtered_walk(path, file_filter): + """ + static method that calls os.walk, but filters out + anything that doesn't match the filter + """ + for root, dirs, files in os.walk(path): + log.debug('looking in %s', root) + log.debug('files is %s', files) + file_filter.set_root(root) + files = filter(file_filter, files) + log.debug('filtered files is %s', files) + yield (root, dirs, files) - def quit(self): - event.SetEvent(self.quit_event) + def quit(self): + event.SetEvent(self.quit_event) class BlockingNotifier(Notifier): + @staticmethod + def wait_results(*args): + """calls WaitForMultipleObjects repeatedly with args""" + return itertools.starmap(event.WaitForMultipleObjects, itertools.repeat(args)) - @staticmethod - def wait_results(*args): - """ calls WaitForMultipleObjects repeatedly with args """ - return itertools.starmap( - event.WaitForMultipleObjects, - itertools.repeat(args)) + def get_changed_files(self): + self._get_change_handle() + check_time = datetime.datetime.utcnow() + # block (sleep) until something changes in the + # target directory or a quit is requested. + # timeout so we can catch keyboard interrupts or other exceptions + events = (self.hChange, self.quit_event) + for result in self.wait_results(events, False, 1000): + if result == event.WAIT_TIMEOUT: + continue + index = result - event.WAIT_OBJECT_0 + if events[index] is self.quit_event: + # quit was received; stop yielding results + return - def get_changed_files(self): - self._get_change_handle() - check_time = datetime.datetime.utcnow() - # block (sleep) until something changes in the - # target directory or a quit is requested. - # timeout so we can catch keyboard interrupts or other exceptions - events = (self.hChange, self.quit_event) - for result in self.wait_results(events, False, 1000): - if result == event.WAIT_TIMEOUT: - continue - index = result - event.WAIT_OBJECT_0 - if events[index] is self.quit_event: - # quit was received; stop yielding results - return + # something has changed. + log.debug('Change notification received') + fs.FindNextChangeNotification(self.hChange) + next_check_time = datetime.datetime.utcnow() + log.debug('Looking for all files changed after %s', check_time) + for file in self.find_files_after(check_time): + yield file + check_time = next_check_time - # something has changed. - log.debug('Change notification received') - fs.FindNextChangeNotification(self.hChange) - next_check_time = datetime.datetime.utcnow() - log.debug('Looking for all files changed after %s', check_time) - for file in self.find_files_after(check_time): - yield file - check_time = next_check_time - - def find_files_after(self, cutoff): - mtf = ModifiedTimeFilter(cutoff) - af = AggregateFilter(mtf, self.opm_filter, *self.filters) - results = Notifier._filtered_walk(self.root, af) - results = itertools.imap(get_file_paths, results) - if self.watch_subtree: - result = itertools.chain(*results) - else: - result = next(results) - return result + def find_files_after(self, cutoff): + mtf = ModifiedTimeFilter(cutoff) + af = AggregateFilter(mtf, self.opm_filter, *self.filters) + results = Notifier._filtered_walk(self.root, af) + results = itertools.imap(get_file_paths, results) + if self.watch_subtree: + result = itertools.chain(*results) + else: + result = next(results) + return result class ThreadedNotifier(BlockingNotifier, Thread): - r""" - ThreadedNotifier provides a simple interface that calls the handler - for each file rooted in root that passes the filters. It runs as its own - thread, so must be started as such:: + r""" + ThreadedNotifier provides a simple interface that calls the handler + for each file rooted in root that passes the filters. It runs as its own + thread, so must be started as such:: - notifier = ThreadedNotifier('c:\\', handler = StreamHandler()) - notifier.start() - C:\Autoexec.bat changed. - """ - def __init__(self, root='.', filters=[], handler=lambda file: None): - # init notifier stuff - BlockingNotifier.__init__(self, root, filters) - # init thread stuff - Thread.__init__(self) - # set it as a daemon thread so that it doesn't block waiting to close. - # I tried setting __del__(self) to .quit(), but unfortunately, there - # are references to this object in the win32api stuff, so __del__ - # never gets called. - self.setDaemon(True) + notifier = ThreadedNotifier('c:\\', handler = StreamHandler()) + notifier.start() + C:\Autoexec.bat changed. + """ - self.handle = handler + def __init__(self, root='.', filters=[], handler=lambda file: None): + # init notifier stuff + BlockingNotifier.__init__(self, root, filters) + # init thread stuff + Thread.__init__(self) + # set it as a daemon thread so that it doesn't block waiting to close. + # I tried setting __del__(self) to .quit(), but unfortunately, there + # are references to this object in the win32api stuff, so __del__ + # never gets called. + self.setDaemon(True) - def run(self): - for file in self.get_changed_files(): - self.handle(file) + self.handle = handler + + def run(self): + for file in self.get_changed_files(): + self.handle(file) class StreamHandler(object): - """ - StreamHandler: a sample handler object for use with the threaded - notifier that will announce by writing to the supplied stream - (stdout by default) the name of the file. - """ - def __init__(self, output=sys.stdout): - self.output = output + """ + StreamHandler: a sample handler object for use with the threaded + notifier that will announce by writing to the supplied stream + (stdout by default) the name of the file. + """ - def __call__(self, filename): - self.output.write('%s changed.\n' % filename) + def __init__(self, output=sys.stdout): + self.output = output + + def __call__(self, filename): + self.output.write('%s changed.\n' % filename) diff --git a/libs/win/jaraco/windows/inet.py b/libs/win/jaraco/windows/inet.py index 37c40cda..4c98dc54 100644 --- a/libs/win/jaraco/windows/inet.py +++ b/libs/win/jaraco/windows/inet.py @@ -3,8 +3,6 @@ Some routines for retrieving the addresses from the local network config. """ -from __future__ import print_function - import itertools import ctypes @@ -13,112 +11,108 @@ from jaraco.windows.api import errors, inet def GetAdaptersAddresses(): - size = ctypes.c_ulong() - res = inet.GetAdaptersAddresses(0, 0, None, None, size) - if res != errors.ERROR_BUFFER_OVERFLOW: - raise RuntimeError("Error getting structure length (%d)" % res) - print(size.value) - pointer_type = ctypes.POINTER(inet.IP_ADAPTER_ADDRESSES) - buffer = ctypes.create_string_buffer(size.value) - struct_p = ctypes.cast(buffer, pointer_type) - res = inet.GetAdaptersAddresses(0, 0, None, struct_p, size) - if res != errors.NO_ERROR: - raise RuntimeError("Error retrieving table (%d)" % res) - while struct_p: - yield struct_p.contents - struct_p = struct_p.contents.next + size = ctypes.c_ulong() + res = inet.GetAdaptersAddresses(0, 0, None, None, size) + if res != errors.ERROR_BUFFER_OVERFLOW: + raise RuntimeError("Error getting structure length (%d)" % res) + print(size.value) + pointer_type = ctypes.POINTER(inet.IP_ADAPTER_ADDRESSES) + buffer = ctypes.create_string_buffer(size.value) + struct_p = ctypes.cast(buffer, pointer_type) + res = inet.GetAdaptersAddresses(0, 0, None, struct_p, size) + if res != errors.NO_ERROR: + raise RuntimeError("Error retrieving table (%d)" % res) + while struct_p: + yield struct_p.contents + struct_p = struct_p.contents.next class AllocatedTable(object): - """ - Both the interface table and the ip address table use the same - technique to store arrays of structures of variable length. This - base class captures the functionality to retrieve and access those - table entries. + """ + Both the interface table and the ip address table use the same + technique to store arrays of structures of variable length. This + base class captures the functionality to retrieve and access those + table entries. - The subclass needs to define three class attributes: - method: a callable that takes three arguments - a pointer to - the structure, the length of the data contained by the - structure, and a boolean of whether the result should - be sorted. - structure: a C structure defininition that describes the table - format. - row_structure: a C structure definition that describes the row - format. - """ - def __get_table_size(self): - """ - Retrieve the size of the buffer needed by calling the method - with a null pointer and length of zero. This should trigger an - insufficient buffer error and return the size needed for the - buffer. - """ - length = ctypes.wintypes.DWORD() - res = self.method(None, length, False) - if res != errors.ERROR_INSUFFICIENT_BUFFER: - raise RuntimeError("Error getting table length (%d)" % res) - return length.value + The subclass needs to define three class attributes: + method: a callable that takes three arguments - a pointer to + the structure, the length of the data contained by the + structure, and a boolean of whether the result should + be sorted. + structure: a C structure defininition that describes the table + format. + row_structure: a C structure definition that describes the row + format. + """ - def get_table(self): - """ - Get the table - """ - buffer_length = self.__get_table_size() - returned_buffer_length = ctypes.wintypes.DWORD(buffer_length) - buffer = ctypes.create_string_buffer(buffer_length) - pointer_type = ctypes.POINTER(self.structure) - table_p = ctypes.cast(buffer, pointer_type) - res = self.method(table_p, returned_buffer_length, False) - if res != errors.NO_ERROR: - raise RuntimeError("Error retrieving table (%d)" % res) - return table_p.contents + def __get_table_size(self): + """ + Retrieve the size of the buffer needed by calling the method + with a null pointer and length of zero. This should trigger an + insufficient buffer error and return the size needed for the + buffer. + """ + length = ctypes.wintypes.DWORD() + res = self.method(None, length, False) + if res != errors.ERROR_INSUFFICIENT_BUFFER: + raise RuntimeError("Error getting table length (%d)" % res) + return length.value - @property - def entries(self): - """ - Using the table structure, return the array of entries based - on the table size. - """ - table = self.get_table() - entries_array = self.row_structure * table.num_entries - pointer_type = ctypes.POINTER(entries_array) - return ctypes.cast(table.entries, pointer_type).contents + def get_table(self): + """ + Get the table + """ + buffer_length = self.__get_table_size() + returned_buffer_length = ctypes.wintypes.DWORD(buffer_length) + buffer = ctypes.create_string_buffer(buffer_length) + pointer_type = ctypes.POINTER(self.structure) + table_p = ctypes.cast(buffer, pointer_type) + res = self.method(table_p, returned_buffer_length, False) + if res != errors.NO_ERROR: + raise RuntimeError("Error retrieving table (%d)" % res) + return table_p.contents + + @property + def entries(self): + """ + Using the table structure, return the array of entries based + on the table size. + """ + table = self.get_table() + entries_array = self.row_structure * table.num_entries + pointer_type = ctypes.POINTER(entries_array) + return ctypes.cast(table.entries, pointer_type).contents class InterfaceTable(AllocatedTable): - method = inet.GetIfTable - structure = inet.MIB_IFTABLE - row_structure = inet.MIB_IFROW + method = inet.GetIfTable + structure = inet.MIB_IFTABLE + row_structure = inet.MIB_IFROW class AddressTable(AllocatedTable): - method = inet.GetIpAddrTable - structure = inet.MIB_IPADDRTABLE - row_structure = inet.MIB_IPADDRROW + method = inet.GetIpAddrTable + structure = inet.MIB_IPADDRTABLE + row_structure = inet.MIB_IPADDRROW class AddressManager(object): - @staticmethod - def hardware_address_to_string(addr): - hex_bytes = (byte.encode('hex') for byte in addr) - return ':'.join(hex_bytes) + @staticmethod + def hardware_address_to_string(addr): + hex_bytes = (byte.encode('hex') for byte in addr) + return ':'.join(hex_bytes) - def get_host_mac_address_strings(self): - return ( - self.hardware_address_to_string(addr) - for addr in self.get_host_mac_addresses()) + def get_host_mac_address_strings(self): + return ( + self.hardware_address_to_string(addr) + for addr in self.get_host_mac_addresses() + ) - def get_host_ip_address_strings(self): - return itertools.imap(str, self.get_host_ip_addresses()) + def get_host_ip_address_strings(self): + return itertools.imap(str, self.get_host_ip_addresses()) - def get_host_mac_addresses(self): - return ( - entry.physical_address - for entry in InterfaceTable().entries - ) + def get_host_mac_addresses(self): + return (entry.physical_address for entry in InterfaceTable().entries) - def get_host_ip_addresses(self): - return ( - entry.address - for entry in AddressTable().entries - ) + def get_host_ip_addresses(self): + return (entry.address for entry in AddressTable().entries) diff --git a/libs/win/jaraco/windows/lib.py b/libs/win/jaraco/windows/lib.py index 0602c8e0..64ebfffb 100644 --- a/libs/win/jaraco/windows/lib.py +++ b/libs/win/jaraco/windows/lib.py @@ -4,18 +4,18 @@ from .api import library def find_lib(lib): - r""" - Find the DLL for a given library. + r""" + Find the DLL for a given library. - Accepts a string or loaded module + Accepts a string or loaded module - >>> print(find_lib('kernel32').lower()) - c:\windows\system32\kernel32.dll - """ - if isinstance(lib, str): - lib = getattr(ctypes.windll, lib) + >>> print(find_lib('kernel32').lower()) + c:\windows\system32\kernel32.dll + """ + if isinstance(lib, str): + lib = getattr(ctypes.windll, lib) - size = 1024 - result = ctypes.create_unicode_buffer(size) - library.GetModuleFileName(lib._handle, result, size) - return result.value + size = 1024 + result = ctypes.create_unicode_buffer(size) + library.GetModuleFileName(lib._handle, result, size) + return result.value diff --git a/libs/win/jaraco/windows/memory.py b/libs/win/jaraco/windows/memory.py index d4bcb83c..1e989376 100644 --- a/libs/win/jaraco/windows/memory.py +++ b/libs/win/jaraco/windows/memory.py @@ -5,25 +5,25 @@ from .api import memory class LockedMemory(object): - def __init__(self, handle): - self.handle = handle + def __init__(self, handle): + self.handle = handle - def __enter__(self): - self.data_ptr = memory.GlobalLock(self.handle) - if not self.data_ptr: - del self.data_ptr - raise WinError() - return self + def __enter__(self): + self.data_ptr = memory.GlobalLock(self.handle) + if not self.data_ptr: + del self.data_ptr + raise WinError() + return self - def __exit__(self, *args): - memory.GlobalUnlock(self.handle) - del self.data_ptr + def __exit__(self, *args): + memory.GlobalUnlock(self.handle) + del self.data_ptr - @property - def data(self): - with self: - return ctypes.string_at(self.data_ptr, self.size) + @property + def data(self): + with self: + return ctypes.string_at(self.data_ptr, self.size) - @property - def size(self): - return memory.GlobalSize(self.data_ptr) + @property + def size(self): + return memory.GlobalSize(self.data_ptr) diff --git a/libs/win/jaraco/windows/mmap.py b/libs/win/jaraco/windows/mmap.py index 11460894..c64c2548 100644 --- a/libs/win/jaraco/windows/mmap.py +++ b/libs/win/jaraco/windows/mmap.py @@ -1,63 +1,66 @@ import ctypes.wintypes -import six - from .error import handle_nonzero_success from .api import memory class MemoryMap(object): - """ - A memory map object which can have security attributes overridden. - """ - def __init__(self, name, length, security_attributes=None): - self.name = name - self.length = length - self.security_attributes = security_attributes - self.pos = 0 + """ + A memory map object which can have security attributes overridden. + """ - def __enter__(self): - p_SA = ( - ctypes.byref(self.security_attributes) - if self.security_attributes else None - ) - INVALID_HANDLE_VALUE = -1 - PAGE_READWRITE = 0x4 - FILE_MAP_WRITE = 0x2 - filemap = ctypes.windll.kernel32.CreateFileMappingW( - INVALID_HANDLE_VALUE, p_SA, PAGE_READWRITE, 0, self.length, - six.text_type(self.name)) - handle_nonzero_success(filemap) - if filemap == INVALID_HANDLE_VALUE: - raise Exception("Failed to create file mapping") - self.filemap = filemap - self.view = memory.MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0) - return self + def __init__(self, name, length, security_attributes=None): + self.name = name + self.length = length + self.security_attributes = security_attributes + self.pos = 0 - def seek(self, pos): - self.pos = pos + def __enter__(self): + p_SA = ( + ctypes.byref(self.security_attributes) if self.security_attributes else None + ) + INVALID_HANDLE_VALUE = -1 + PAGE_READWRITE = 0x4 + FILE_MAP_WRITE = 0x2 + filemap = ctypes.windll.kernel32.CreateFileMappingW( + INVALID_HANDLE_VALUE, + p_SA, + PAGE_READWRITE, + 0, + self.length, + str(self.name), + ) + handle_nonzero_success(filemap) + if filemap == INVALID_HANDLE_VALUE: + raise Exception("Failed to create file mapping") + self.filemap = filemap + self.view = memory.MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0) + return self - def write(self, msg): - assert isinstance(msg, bytes) - n = len(msg) - if self.pos + n >= self.length: # A little safety. - raise ValueError("Refusing to write %d bytes" % n) - dest = self.view + self.pos - length = ctypes.c_size_t(n) - ctypes.windll.kernel32.RtlMoveMemory(dest, msg, length) - self.pos += n + def seek(self, pos): + self.pos = pos - def read(self, n): - """ - Read n bytes from mapped view. - """ - out = ctypes.create_string_buffer(n) - source = self.view + self.pos - length = ctypes.c_size_t(n) - ctypes.windll.kernel32.RtlMoveMemory(out, source, length) - self.pos += n - return out.raw + def write(self, msg): + assert isinstance(msg, bytes) + n = len(msg) + if self.pos + n >= self.length: # A little safety. + raise ValueError("Refusing to write %d bytes" % n) + dest = self.view + self.pos + length = ctypes.c_size_t(n) + ctypes.windll.kernel32.RtlMoveMemory(dest, msg, length) + self.pos += n - def __exit__(self, exc_type, exc_val, tb): - ctypes.windll.kernel32.UnmapViewOfFile(self.view) - ctypes.windll.kernel32.CloseHandle(self.filemap) + def read(self, n): + """ + Read n bytes from mapped view. + """ + out = ctypes.create_string_buffer(n) + source = self.view + self.pos + length = ctypes.c_size_t(n) + ctypes.windll.kernel32.RtlMoveMemory(out, source, length) + self.pos += n + return out.raw + + def __exit__(self, exc_type, exc_val, tb): + ctypes.windll.kernel32.UnmapViewOfFile(self.view) + ctypes.windll.kernel32.CloseHandle(self.filemap) diff --git a/libs/win/jaraco/windows/msie.py b/libs/win/jaraco/windows/msie.py index c4b5793c..d4136182 100644 --- a/libs/win/jaraco/windows/msie.py +++ b/libs/win/jaraco/windows/msie.py @@ -1,5 +1,3 @@ -# -*- coding: UTF-8 -*- - """cookies.py Cookie support utilities @@ -8,52 +6,50 @@ Cookie support utilities import os import itertools -import six - class CookieMonster(object): - "Read cookies out of a user's IE cookies file" + "Read cookies out of a user's IE cookies file" - @property - def cookie_dir(self): - import _winreg as winreg - key = winreg.OpenKeyEx( - winreg.HKEY_CURRENT_USER, 'Software' - '\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders') - cookie_dir, type = winreg.QueryValueEx(key, 'Cookies') - return cookie_dir + @property + def cookie_dir(self): + import _winreg as winreg - def entries(self, filename): - with open(os.path.join(self.cookie_dir, filename)) as cookie_file: - while True: - entry = itertools.takewhile( - self.is_not_cookie_delimiter, - cookie_file) - entry = list(map(six.text_type.rstrip, entry)) - if not entry: - break - cookie = self.make_cookie(*entry) - yield cookie + key = winreg.OpenKeyEx( + winreg.HKEY_CURRENT_USER, + 'Software' r'\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders', + ) + cookie_dir, type = winreg.QueryValueEx(key, 'Cookies') + return cookie_dir - @staticmethod - def is_not_cookie_delimiter(s): - return s != '*\n' + def entries(self, filename): + with open(os.path.join(self.cookie_dir, filename)) as cookie_file: + while True: + entry = itertools.takewhile(self.is_not_cookie_delimiter, cookie_file) + entry = [item.rstrip() for item in entry] + if not entry: + break + cookie = self.make_cookie(*entry) + yield cookie - @staticmethod - def make_cookie( - key, value, domain, flags, ExpireLow, ExpireHigh, - CreateLow, CreateHigh): - expires = (int(ExpireHigh) << 32) | int(ExpireLow) - created = (int(CreateHigh) << 32) | int(CreateLow) - flags = int(flags) - domain, sep, path = domain.partition('/') - path = '/' + path - return dict( - key=key, - value=value, - domain=domain, - flags=flags, - expires=expires, - created=created, - path=path, - ) + @staticmethod + def is_not_cookie_delimiter(s): + return s != '*\n' + + @staticmethod + def make_cookie( + key, value, domain, flags, ExpireLow, ExpireHigh, CreateLow, CreateHigh + ): + expires = (int(ExpireHigh) << 32) | int(ExpireLow) + created = (int(CreateHigh) << 32) | int(CreateLow) + flags = int(flags) + domain, sep, path = domain.partition('/') + path = '/' + path + return dict( + key=key, + value=value, + domain=domain, + flags=flags, + expires=expires, + created=created, + path=path, + ) diff --git a/libs/win/jaraco/windows/msvc.py b/libs/win/jaraco/windows/msvc.py new file mode 100644 index 00000000..b060c7b0 --- /dev/null +++ b/libs/win/jaraco/windows/msvc.py @@ -0,0 +1,37 @@ +import subprocess + + +default_components = [ + 'Microsoft.VisualStudio.Component.CoreEditor', + 'Microsoft.VisualStudio.Workload.CoreEditor', + 'Microsoft.VisualStudio.Component.Roslyn.Compiler', + 'Microsoft.Component.MSBuild', + 'Microsoft.VisualStudio.Component.TextTemplating', + 'Microsoft.VisualStudio.Component.VC.CoreIde', + 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', + 'Microsoft.VisualStudio.Component.Windows10SDK.19041', + 'Microsoft.VisualStudio.Component.VC.Redist.14.Latest', + 'Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core', + 'Microsoft.VisualStudio.Workload.NativeDesktop', +] + + +def install(components=default_components): + cmd = [ + 'vs_buildtools', + '--quiet', + '--wait', + '--norestart', + '--nocache', + '--installPath', + 'C:\\BuildTools', + ] + for component in components: + cmd += ['--add', component] + res = subprocess.Popen(cmd).wait() + if res != 3010: + raise SystemExit(res) + + +__name__ == '__main__' and install() diff --git a/libs/win/jaraco/windows/net.py b/libs/win/jaraco/windows/net.py index 709f0dbf..4057a5f3 100644 --- a/libs/win/jaraco/windows/net.py +++ b/libs/win/jaraco/windows/net.py @@ -2,29 +2,30 @@ API hooks for network stuff. """ -__all__ = ('AddConnection') +__all__ = 'AddConnection' from jaraco.windows.error import WindowsError from .api import net def AddConnection( - remote_name, type=net.RESOURCETYPE_ANY, local_name=None, - provider_name=None, user=None, password=None, flags=0): - resource = net.NETRESOURCE( - type=type, - remote_name=remote_name, - local_name=local_name, - provider_name=provider_name, - # WNetAddConnection2 ignores the other members of NETRESOURCE - ) + remote_name, + type=net.RESOURCETYPE_ANY, + local_name=None, + provider_name=None, + user=None, + password=None, + flags=0, +): + resource = net.NETRESOURCE( + type=type, + remote_name=remote_name, + local_name=local_name, + provider_name=provider_name, + # WNetAddConnection2 ignores the other members of NETRESOURCE + ) - result = net.WNetAddConnection2( - resource, - password, - user, - flags, - ) + result = net.WNetAddConnection2(resource, password, user, flags) - if result != 0: - raise WindowsError(result) + if result != 0: + raise WindowsError(result) diff --git a/libs/win/jaraco/windows/power.py b/libs/win/jaraco/windows/power.py index 8d8276fa..f88f1308 100644 --- a/libs/win/jaraco/windows/power.py +++ b/libs/win/jaraco/windows/power.py @@ -1,77 +1,75 @@ -# -*- coding: utf-8 -*- - -from __future__ import print_function - import itertools import contextlib from more_itertools.recipes import consume, unique_justseen + try: - import wmi as wmilib + import wmi as wmilib except ImportError: - pass + pass from jaraco.windows.error import handle_nonzero_success from .api import power def GetSystemPowerStatus(): - stat = power.SYSTEM_POWER_STATUS() - handle_nonzero_success(GetSystemPowerStatus(stat)) - return stat + stat = power.SYSTEM_POWER_STATUS() + handle_nonzero_success(GetSystemPowerStatus(stat)) + return stat def _init_power_watcher(): - global power_watcher - if 'power_watcher' not in globals(): - wmi = wmilib.WMI() - query = 'SELECT * from Win32_PowerManagementEvent' - power_watcher = wmi.ExecNotificationQuery(query) + global power_watcher + if 'power_watcher' not in globals(): + wmi = wmilib.WMI() + query = 'SELECT * from Win32_PowerManagementEvent' + power_watcher = wmi.ExecNotificationQuery(query) def get_power_management_events(): - _init_power_watcher() - while True: - yield power_watcher.NextEvent() + _init_power_watcher() + while True: + yield power_watcher.NextEvent() def wait_for_power_status_change(): - EVT_POWER_STATUS_CHANGE = 10 + EVT_POWER_STATUS_CHANGE = 10 - def not_power_status_change(evt): - return evt.EventType != EVT_POWER_STATUS_CHANGE - events = get_power_management_events() - consume(itertools.takewhile(not_power_status_change, events)) + def not_power_status_change(evt): + return evt.EventType != EVT_POWER_STATUS_CHANGE + + events = get_power_management_events() + consume(itertools.takewhile(not_power_status_change, events)) def get_unique_power_states(): - """ - Just like get_power_states, but ensures values are returned only - when the state changes. - """ - return unique_justseen(get_power_states()) + """ + Just like get_power_states, but ensures values are returned only + when the state changes. + """ + return unique_justseen(get_power_states()) def get_power_states(): - """ - Continuously return the power state of the system when it changes. - This function will block indefinitely if the power state never - changes. - """ - while True: - state = GetSystemPowerStatus() - yield state.ac_line_status_string - wait_for_power_status_change() + """ + Continuously return the power state of the system when it changes. + This function will block indefinitely if the power state never + changes. + """ + while True: + state = GetSystemPowerStatus() + yield state.ac_line_status_string + wait_for_power_status_change() @contextlib.contextmanager def no_sleep(): - """ - Context that prevents the computer from going to sleep. - """ - mode = power.ES.continuous | power.ES.system_required - handle_nonzero_success(power.SetThreadExecutionState(mode)) - try: - yield - finally: - handle_nonzero_success(power.SetThreadExecutionState(power.ES.continuous)) + """ + Context that prevents the computer from going to sleep. + """ + mode = power.ES.continuous | power.ES.system_required + handle_nonzero_success(power.SetThreadExecutionState(mode)) + try: + yield + finally: + handle_nonzero_success(power.SetThreadExecutionState(power.ES.continuous)) diff --git a/libs/win/jaraco/windows/privilege.py b/libs/win/jaraco/windows/privilege.py index 848a526d..7a75bcfa 100644 --- a/libs/win/jaraco/windows/privilege.py +++ b/libs/win/jaraco/windows/privilege.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import ctypes from ctypes import wintypes @@ -9,134 +7,138 @@ from .api import process def get_process_token(): - """ - Get the current process token - """ - token = wintypes.HANDLE() - res = process.OpenProcessToken( - process.GetCurrentProcess(), process.TOKEN_ALL_ACCESS, token) - if not res > 0: - raise RuntimeError("Couldn't get process token") - return token + """ + Get the current process token + """ + token = wintypes.HANDLE() + res = process.OpenProcessToken( + process.GetCurrentProcess(), process.TOKEN_ALL_ACCESS, token + ) + if not res > 0: + raise RuntimeError("Couldn't get process token") + return token def get_symlink_luid(): - """ - Get the LUID for the SeCreateSymbolicLinkPrivilege - """ - symlink_luid = privilege.LUID() - res = privilege.LookupPrivilegeValue( - None, "SeCreateSymbolicLinkPrivilege", symlink_luid) - if not res > 0: - raise RuntimeError("Couldn't lookup privilege value") - return symlink_luid + """ + Get the LUID for the SeCreateSymbolicLinkPrivilege + """ + symlink_luid = privilege.LUID() + res = privilege.LookupPrivilegeValue( + None, "SeCreateSymbolicLinkPrivilege", symlink_luid + ) + if not res > 0: + raise RuntimeError("Couldn't lookup privilege value") + return symlink_luid def get_privilege_information(): - """ - Get all privileges associated with the current process. - """ - # first call with zero length to determine what size buffer we need + """ + Get all privileges associated with the current process. + """ + # first call with zero length to determine what size buffer we need - return_length = wintypes.DWORD() - params = [ - get_process_token(), - privilege.TOKEN_INFORMATION_CLASS.TokenPrivileges, - None, - 0, - return_length, - ] + return_length = wintypes.DWORD() + params = [ + get_process_token(), + privilege.TOKEN_INFORMATION_CLASS.TokenPrivileges, + None, + 0, + return_length, + ] - res = privilege.GetTokenInformation(*params) + res = privilege.GetTokenInformation(*params) - # assume we now have the necessary length in return_length + # assume we now have the necessary length in return_length - buffer = ctypes.create_string_buffer(return_length.value) - params[2] = buffer - params[3] = return_length.value + buffer = ctypes.create_string_buffer(return_length.value) + params[2] = buffer + params[3] = return_length.value - res = privilege.GetTokenInformation(*params) - assert res > 0, "Error in second GetTokenInformation (%d)" % res + res = privilege.GetTokenInformation(*params) + assert res > 0, "Error in second GetTokenInformation (%d)" % res - privileges = ctypes.cast( - buffer, ctypes.POINTER(privilege.TOKEN_PRIVILEGES)).contents - return privileges + privileges = ctypes.cast( + buffer, ctypes.POINTER(privilege.TOKEN_PRIVILEGES) + ).contents + return privileges def report_privilege_information(): - """ - Report all privilege information assigned to the current process. - """ - privileges = get_privilege_information() - print("found {0} privileges".format(privileges.count)) - tuple(map(print, privileges)) + """ + Report all privilege information assigned to the current process. + """ + privileges = get_privilege_information() + print("found {0} privileges".format(privileges.count)) + tuple(map(print, privileges)) def enable_symlink_privilege(): - """ - Try to assign the symlink privilege to the current process token. - Return True if the assignment is successful. - """ - # create a space in memory for a TOKEN_PRIVILEGES structure - # with one element - size = ctypes.sizeof(privilege.TOKEN_PRIVILEGES) - size += ctypes.sizeof(privilege.LUID_AND_ATTRIBUTES) - buffer = ctypes.create_string_buffer(size) - tp = ctypes.cast(buffer, ctypes.POINTER(privilege.TOKEN_PRIVILEGES)).contents - tp.count = 1 - tp.get_array()[0].enable() - tp.get_array()[0].LUID = get_symlink_luid() - token = get_process_token() - res = privilege.AdjustTokenPrivileges(token, False, tp, 0, None, None) - if res == 0: - raise RuntimeError("Error in AdjustTokenPrivileges") + """ + Try to assign the symlink privilege to the current process token. + Return True if the assignment is successful. + """ + # create a space in memory for a TOKEN_PRIVILEGES structure + # with one element + size = ctypes.sizeof(privilege.TOKEN_PRIVILEGES) + size += ctypes.sizeof(privilege.LUID_AND_ATTRIBUTES) + buffer = ctypes.create_string_buffer(size) + tp = ctypes.cast(buffer, ctypes.POINTER(privilege.TOKEN_PRIVILEGES)).contents + tp.count = 1 + tp.get_array()[0].enable() + tp.get_array()[0].LUID = get_symlink_luid() + token = get_process_token() + res = privilege.AdjustTokenPrivileges(token, False, tp, 0, None, None) + if res == 0: + raise RuntimeError("Error in AdjustTokenPrivileges") - ERROR_NOT_ALL_ASSIGNED = 1300 - return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED + ERROR_NOT_ALL_ASSIGNED = 1300 + return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED class PolicyHandle(wintypes.HANDLE): - pass + pass class LSA_UNICODE_STRING(ctypes.Structure): - _fields_ = [ - ('length', ctypes.c_ushort), - ('max_length', ctypes.c_ushort), - ('buffer', ctypes.wintypes.LPWSTR), - ] + _fields_ = [ + ('length', ctypes.c_ushort), + ('max_length', ctypes.c_ushort), + ('buffer', ctypes.wintypes.LPWSTR), + ] def OpenPolicy(system_name, object_attributes, access_mask): - policy = PolicyHandle() - raise NotImplementedError( - "Need to construct structures for parameters " - "(see http://msdn.microsoft.com/en-us/library/windows" - "/desktop/aa378299%28v=vs.85%29.aspx)") - res = ctypes.windll.advapi32.LsaOpenPolicy( - system_name, object_attributes, - access_mask, ctypes.byref(policy)) - assert res == 0, "Error status {res}".format(**vars()) - return policy + policy = PolicyHandle() + raise NotImplementedError( + "Need to construct structures for parameters " + "(see http://msdn.microsoft.com/en-us/library/windows" + "/desktop/aa378299%28v=vs.85%29.aspx)" + ) + res = ctypes.windll.advapi32.LsaOpenPolicy( + system_name, object_attributes, access_mask, ctypes.byref(policy) + ) + assert res == 0, "Error status {res}".format(**vars()) + return policy def grant_symlink_privilege(who, machine=''): - """ - Grant the 'create symlink' privilege to who. + """ + Grant the 'create symlink' privilege to who. - Based on http://support.microsoft.com/kb/132958 - """ - flags = security.POLICY_CREATE_ACCOUNT | security.POLICY_LOOKUP_NAMES - policy = OpenPolicy(machine, flags) - return policy + Based on http://support.microsoft.com/kb/132958 + """ + flags = security.POLICY_CREATE_ACCOUNT | security.POLICY_LOOKUP_NAMES + policy = OpenPolicy(machine, flags) + return policy def main(): - assigned = enable_symlink_privilege() - msg = ['failure', 'success'][assigned] + assigned = enable_symlink_privilege() + msg = ['failure', 'success'][assigned] - print("Symlink privilege assignment completed with {0}".format(msg)) + print("Symlink privilege assignment completed with {0}".format(msg)) if __name__ == '__main__': - main() + main() diff --git a/libs/win/jaraco/windows/registry.py b/libs/win/jaraco/windows/registry.py index b6f3b239..dd4b6848 100644 --- a/libs/win/jaraco/windows/registry.py +++ b/libs/win/jaraco/windows/registry.py @@ -1,20 +1,18 @@ +import winreg from itertools import count -import six -winreg = six.moves.winreg - def key_values(key): - for index in count(): - try: - yield winreg.EnumValue(key, index) - except WindowsError: - break + for index in count(): + try: + yield winreg.EnumValue(key, index) + except WindowsError: + break def key_subkeys(key): - for index in count(): - try: - yield winreg.EnumKey(key, index) - except WindowsError: - break + for index in count(): + try: + yield winreg.EnumKey(key, index) + except WindowsError: + break diff --git a/libs/win/jaraco/windows/reparse.py b/libs/win/jaraco/windows/reparse.py index 2751e967..f9159381 100644 --- a/libs/win/jaraco/windows/reparse.py +++ b/libs/win/jaraco/windows/reparse.py @@ -1,35 +1,34 @@ -from __future__ import division - import ctypes.wintypes from .error import handle_nonzero_success from .api import filesystem -def DeviceIoControl( - device, io_control_code, in_buffer, out_buffer, overlapped=None): - if overlapped is not None: - raise NotImplementedError("overlapped handles not yet supported") +def DeviceIoControl(device, io_control_code, in_buffer, out_buffer, overlapped=None): + if overlapped is not None: + raise NotImplementedError("overlapped handles not yet supported") - if isinstance(out_buffer, int): - out_buffer = ctypes.create_string_buffer(out_buffer) + if isinstance(out_buffer, int): + out_buffer = ctypes.create_string_buffer(out_buffer) - in_buffer_size = len(in_buffer) if in_buffer is not None else 0 - out_buffer_size = len(out_buffer) - assert isinstance(out_buffer, ctypes.Array) + in_buffer_size = len(in_buffer) if in_buffer is not None else 0 + out_buffer_size = len(out_buffer) + assert isinstance(out_buffer, ctypes.Array) - returned_bytes = ctypes.wintypes.DWORD() + returned_bytes = ctypes.wintypes.DWORD() - res = filesystem.DeviceIoControl( - device, - io_control_code, - in_buffer, in_buffer_size, - out_buffer, out_buffer_size, - returned_bytes, - overlapped, - ) + res = filesystem.DeviceIoControl( + device, + io_control_code, + in_buffer, + in_buffer_size, + out_buffer, + out_buffer_size, + returned_bytes, + overlapped, + ) - handle_nonzero_success(res) - handle_nonzero_success(returned_bytes) + handle_nonzero_success(res) + handle_nonzero_success(returned_bytes) - return out_buffer[:returned_bytes.value] + return out_buffer[: returned_bytes.value] diff --git a/libs/win/jaraco/windows/security.py b/libs/win/jaraco/windows/security.py index 7c481ed6..43582e04 100644 --- a/libs/win/jaraco/windows/security.py +++ b/libs/win/jaraco/windows/security.py @@ -5,63 +5,66 @@ from .api import security def GetTokenInformation(token, information_class): - """ - Given a token, get the token information for it. - """ - data_size = ctypes.wintypes.DWORD() - ctypes.windll.advapi32.GetTokenInformation( - token, information_class.num, - 0, 0, ctypes.byref(data_size)) - data = ctypes.create_string_buffer(data_size.value) - handle_nonzero_success(ctypes.windll.advapi32.GetTokenInformation( - token, - information_class.num, - ctypes.byref(data), ctypes.sizeof(data), - ctypes.byref(data_size))) - return ctypes.cast(data, ctypes.POINTER(security.TOKEN_USER)).contents + """ + Given a token, get the token information for it. + """ + data_size = ctypes.wintypes.DWORD() + ctypes.windll.advapi32.GetTokenInformation( + token, information_class.num, 0, 0, ctypes.byref(data_size) + ) + data = ctypes.create_string_buffer(data_size.value) + handle_nonzero_success( + ctypes.windll.advapi32.GetTokenInformation( + token, + information_class.num, + ctypes.byref(data), + ctypes.sizeof(data), + ctypes.byref(data_size), + ) + ) + return ctypes.cast(data, ctypes.POINTER(security.TOKEN_USER)).contents def OpenProcessToken(proc_handle, access): - result = ctypes.wintypes.HANDLE() - proc_handle = ctypes.wintypes.HANDLE(proc_handle) - handle_nonzero_success(ctypes.windll.advapi32.OpenProcessToken( - proc_handle, access, ctypes.byref(result))) - return result + result = ctypes.wintypes.HANDLE() + proc_handle = ctypes.wintypes.HANDLE(proc_handle) + handle_nonzero_success( + ctypes.windll.advapi32.OpenProcessToken( + proc_handle, access, ctypes.byref(result) + ) + ) + return result def get_current_user(): - """ - Return a TOKEN_USER for the owner of this process. - """ - process = OpenProcessToken( - ctypes.windll.kernel32.GetCurrentProcess(), - security.TokenAccess.TOKEN_QUERY, - ) - return GetTokenInformation(process, security.TOKEN_USER) + """ + Return a TOKEN_USER for the owner of this process. + """ + process = OpenProcessToken( + ctypes.windll.kernel32.GetCurrentProcess(), security.TokenAccess.TOKEN_QUERY + ) + return GetTokenInformation(process, security.TOKEN_USER) def get_security_attributes_for_user(user=None): - """ - Return a SECURITY_ATTRIBUTES structure with the SID set to the - specified user (uses current user if none is specified). - """ - if user is None: - user = get_current_user() + """ + Return a SECURITY_ATTRIBUTES structure with the SID set to the + specified user (uses current user if none is specified). + """ + if user is None: + user = get_current_user() - assert isinstance(user, security.TOKEN_USER), ( - "user must be TOKEN_USER instance") + assert isinstance(user, security.TOKEN_USER), "user must be TOKEN_USER instance" - SD = security.SECURITY_DESCRIPTOR() - SA = security.SECURITY_ATTRIBUTES() - # by attaching the actual security descriptor, it will be garbage- - # collected with the security attributes - SA.descriptor = SD - SA.bInheritHandle = 1 + SD = security.SECURITY_DESCRIPTOR() + SA = security.SECURITY_ATTRIBUTES() + # by attaching the actual security descriptor, it will be garbage- + # collected with the security attributes + SA.descriptor = SD + SA.bInheritHandle = 1 - ctypes.windll.advapi32.InitializeSecurityDescriptor( - ctypes.byref(SD), - security.SECURITY_DESCRIPTOR.REVISION) - ctypes.windll.advapi32.SetSecurityDescriptorOwner( - ctypes.byref(SD), - user.SID, 0) - return SA + ctypes.windll.advapi32.InitializeSecurityDescriptor( + ctypes.byref(SD), security.SECURITY_DESCRIPTOR.REVISION + ) + ctypes.windll.advapi32.SetSecurityDescriptorOwner(ctypes.byref(SD), user.SID, 0) + return SA diff --git a/libs/win/jaraco/windows/services.py b/libs/win/jaraco/windows/services.py index 97cea7ab..b2064dcd 100644 --- a/libs/win/jaraco/windows/services.py +++ b/libs/win/jaraco/windows/services.py @@ -5,8 +5,6 @@ Based on http://code.activestate.com /recipes/115875-controlling-windows-services/ """ -from __future__ import print_function - import sys import time @@ -16,221 +14,240 @@ import win32service class Service(object): - """ - The Service Class is used for controlling Windows - services. Just pass the name of the service you wish to control to the - class instance and go from there. For example, if you want to control - the Workstation service try this: + """ + The Service Class is used for controlling Windows + services. Just pass the name of the service you wish to control to the + class instance and go from there. For example, if you want to control + the Workstation service try this: - from jaraco.windows import services - workstation = services.Service("Workstation") - workstation.start() - workstation.fetchstatus("running", 10) - workstation.stop() - workstation.fetchstatus("stopped") + from jaraco.windows import services + workstation = services.Service("Workstation") + workstation.start() + workstation.fetchstatus("running", 10) + workstation.stop() + workstation.fetchstatus("stopped") - Creating an instance of the Service class is done by passing the name of - the service as it appears in the Management Console or the short name as - it appears in the registry. Mixed case is ok. - cvs = services.Service("CVS NT Service 1.11.1.2 (Build 41)") - or - cvs = services.Service("cvs") + Creating an instance of the Service class is done by passing the name of + the service as it appears in the Management Console or the short name as + it appears in the registry. Mixed case is ok. + cvs = services.Service("CVS NT Service 1.11.1.2 (Build 41)") + or + cvs = services.Service("cvs") - If needing remote service control try this: - cvs = services.Service("cvs", r"\\CVS_SERVER") - or - cvs = services.Service("cvs", "\\\\CVS_SERVER") + If needing remote service control try this: + cvs = services.Service("cvs", r"\\CVS_SERVER") + or + cvs = services.Service("cvs", "\\\\CVS_SERVER") - The Service Class supports these methods: + The Service Class supports these methods: - start: Starts service. - stop: Stops service. - restart: Stops and restarts service. - pause: Pauses service (Only if service supports feature). - resume: Resumes service that has been paused. - status: Queries current status of service. - fetchstatus: Continually queries service until requested - status(STARTING, RUNNING, - STOPPING & STOPPED) is met or timeout value(in seconds) reached. - Default timeout value is infinite. - infotype: Queries service for process type. (Single, shared and/or - interactive process) - infoctrl: Queries control information about a running service. - i.e. Can it be paused, stopped, etc? - infostartup: Queries service Startup type. (Boot, System, - Automatic, Manual, Disabled) - setstartup Changes/sets Startup type. (Boot, System, - Automatic, Manual, Disabled) - getname: Gets the long and short service names used by Windowin32service. - (Generally used for internal purposes) - """ + start: Starts service. + stop: Stops service. + restart: Stops and restarts service. + pause: Pauses service (Only if service supports feature). + resume: Resumes service that has been paused. + status: Queries current status of service. + fetchstatus: Continually queries service until requested + status(STARTING, RUNNING, + STOPPING & STOPPED) is met or timeout value(in seconds) reached. + Default timeout value is infinite. + infotype: Queries service for process type. (Single, shared and/or + interactive process) + infoctrl: Queries control information about a running service. + i.e. Can it be paused, stopped, etc? + infostartup: Queries service Startup type. (Boot, System, + Automatic, Manual, Disabled) + setstartup: Changes/sets Startup type. (Boot, System, + Automatic, Manual, Disabled) + getname: Gets the long and short service names used by Windowin32service. + (Generally used for internal purposes) + """ - def __init__(self, service, machinename=None, dbname=None): - self.userv = service - self.scmhandle = win32service.OpenSCManager( - machinename, dbname, win32service.SC_MANAGER_ALL_ACCESS) - self.sserv, self.lserv = self.getname() - if (self.sserv or self.lserv) is None: - sys.exit() - self.handle = win32service.OpenService( - self.scmhandle, self.sserv, win32service.SERVICE_ALL_ACCESS) - self.sccss = "SYSTEM\\CurrentControlSet\\Services\\" + def __init__(self, service, machinename=None, dbname=None): + self.userv = service + self.scmhandle = win32service.OpenSCManager( + machinename, dbname, win32service.SC_MANAGER_ALL_ACCESS + ) + self.sserv, self.lserv = self.getname() + if (self.sserv or self.lserv) is None: + sys.exit() + self.handle = win32service.OpenService( + self.scmhandle, self.sserv, win32service.SERVICE_ALL_ACCESS + ) + self.sccss = "SYSTEM\\CurrentControlSet\\Services\\" - def start(self): - win32service.StartService(self.handle, None) + def start(self): + win32service.StartService(self.handle, None) - def stop(self): - self.stat = win32service.ControlService( - self.handle, win32service.SERVICE_CONTROL_STOP) + def stop(self): + self.stat = win32service.ControlService( + self.handle, win32service.SERVICE_CONTROL_STOP + ) - def restart(self): - self.stop() - self.fetchstatus("STOPPED") - self.start() + def restart(self): + self.stop() + self.fetchstatus("STOPPED") + self.start() - def pause(self): - self.stat = win32service.ControlService( - self.handle, win32service.SERVICE_CONTROL_PAUSE) + def pause(self): + self.stat = win32service.ControlService( + self.handle, win32service.SERVICE_CONTROL_PAUSE + ) - def resume(self): - self.stat = win32service.ControlService( - self.handle, win32service.SERVICE_CONTROL_CONTINUE) + def resume(self): + self.stat = win32service.ControlService( + self.handle, win32service.SERVICE_CONTROL_CONTINUE + ) - def status(self, prn=0): - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[1] == win32service.SERVICE_STOPPED: - if prn == 1: - print("The %s service is stopped." % self.lserv) - else: - return "STOPPED" - elif self.stat[1] == win32service.SERVICE_START_PENDING: - if prn == 1: - print("The %s service is starting." % self.lserv) - else: - return "STARTING" - elif self.stat[1] == win32service.SERVICE_STOP_PENDING: - if prn == 1: - print("The %s service is stopping." % self.lserv) - else: - return "STOPPING" - elif self.stat[1] == win32service.SERVICE_RUNNING: - if prn == 1: - print("The %s service is running." % self.lserv) - else: - return "RUNNING" + def status(self, prn=0): + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[1] == win32service.SERVICE_STOPPED: + if prn == 1: + print("The %s service is stopped." % self.lserv) + else: + return "STOPPED" + elif self.stat[1] == win32service.SERVICE_START_PENDING: + if prn == 1: + print("The %s service is starting." % self.lserv) + else: + return "STARTING" + elif self.stat[1] == win32service.SERVICE_STOP_PENDING: + if prn == 1: + print("The %s service is stopping." % self.lserv) + else: + return "STOPPING" + elif self.stat[1] == win32service.SERVICE_RUNNING: + if prn == 1: + print("The %s service is running." % self.lserv) + else: + return "RUNNING" - def fetchstatus(self, fstatus, timeout=None): - self.fstatus = fstatus.upper() - if timeout is not None: - timeout = int(timeout) - timeout *= 2 + def fetchstatus(self, fstatus, timeout=None): + self.fstatus = fstatus.upper() + if timeout is not None: + timeout = int(timeout) + timeout *= 2 - def to(timeout): - time.sleep(.5) - if timeout is not None: - if timeout > 1: - timeout -= 1 - return timeout - else: - return "TO" - if self.fstatus == "STOPPED": - while 1: - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[1] == win32service.SERVICE_STOPPED: - self.fstate = "STOPPED" - break - else: - timeout = to(timeout) - if timeout == "TO": - return "TIMEDOUT" - break - elif self.fstatus == "STOPPING": - while 1: - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[1]==win32service.SERVICE_STOP_PENDING: - self.fstate = "STOPPING" - break - else: - timeout=to(timeout) - if timeout == "TO": - return "TIMEDOUT" - break - elif self.fstatus == "RUNNING": - while 1: - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[1]==win32service.SERVICE_RUNNING: - self.fstate = "RUNNING" - break - else: - timeout=to(timeout) - if timeout == "TO": - return "TIMEDOUT" - break - elif self.fstatus == "STARTING": - while 1: - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[1]==win32service.SERVICE_START_PENDING: - self.fstate = "STARTING" - break - else: - timeout=to(timeout) - if timeout == "TO": - return "TIMEDOUT" - break + def to(timeout): + time.sleep(0.5) + if timeout is not None: + if timeout > 1: + timeout -= 1 + return timeout + else: + return "TO" - def infotype(self): - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[0] and win32service.SERVICE_WIN32_OWN_PROCESS: - print("The %s service runs in its own process." % self.lserv) - if self.stat[0] and win32service.SERVICE_WIN32_SHARE_PROCESS: - print("The %s service shares a process with other services." % self.lserv) - if self.stat[0] and win32service.SERVICE_INTERACTIVE_PROCESS: - print("The %s service can interact with the desktop." % self.lserv) + if self.fstatus == "STOPPED": + while 1: + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[1] == win32service.SERVICE_STOPPED: + self.fstate = "STOPPED" + break + else: + timeout = to(timeout) + if timeout == "TO": + return "TIMEDOUT" + break + elif self.fstatus == "STOPPING": + while 1: + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[1] == win32service.SERVICE_STOP_PENDING: + self.fstate = "STOPPING" + break + else: + timeout = to(timeout) + if timeout == "TO": + return "TIMEDOUT" + break + elif self.fstatus == "RUNNING": + while 1: + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[1] == win32service.SERVICE_RUNNING: + self.fstate = "RUNNING" + break + else: + timeout = to(timeout) + if timeout == "TO": + return "TIMEDOUT" + break + elif self.fstatus == "STARTING": + while 1: + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[1] == win32service.SERVICE_START_PENDING: + self.fstate = "STARTING" + break + else: + timeout = to(timeout) + if timeout == "TO": + return "TIMEDOUT" + break - def infoctrl(self): - self.stat = win32service.QueryServiceStatus(self.handle) - if self.stat[2] and win32service.SERVICE_ACCEPT_PAUSE_CONTINUE: - print("The %s service can be paused." % self.lserv) - if self.stat[2] and win32service.SERVICE_ACCEPT_STOP: - print("The %s service can be stopped." % self.lserv) - if self.stat[2] and win32service.SERVICE_ACCEPT_SHUTDOWN: - print("The %s service can be shutdown." % self.lserv) + def infotype(self): + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[0] and win32service.SERVICE_WIN32_OWN_PROCESS: + print("The %s service runs in its own process." % self.lserv) + if self.stat[0] and win32service.SERVICE_WIN32_SHARE_PROCESS: + print("The %s service shares a process with other services." % self.lserv) + if self.stat[0] and win32service.SERVICE_INTERACTIVE_PROCESS: + print("The %s service can interact with the desktop." % self.lserv) - def infostartup(self): - self.isuphandle = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, self.sccss + self.sserv, 0, win32con.KEY_READ) - self.isuptype = win32api.RegQueryValueEx(self.isuphandle, "Start")[0] - win32api.RegCloseKey(self.isuphandle) - if self.isuptype == 0: - return "boot" - elif self.isuptype == 1: - return "system" - elif self.isuptype == 2: - return "automatic" - elif self.isuptype == 3: - return "manual" - elif self.isuptype == 4: - return "disabled" + def infoctrl(self): + self.stat = win32service.QueryServiceStatus(self.handle) + if self.stat[2] and win32service.SERVICE_ACCEPT_PAUSE_CONTINUE: + print("The %s service can be paused." % self.lserv) + if self.stat[2] and win32service.SERVICE_ACCEPT_STOP: + print("The %s service can be stopped." % self.lserv) + if self.stat[2] and win32service.SERVICE_ACCEPT_SHUTDOWN: + print("The %s service can be shutdown." % self.lserv) - @property - def suptype(self): - types = 'boot', 'system', 'automatic', 'manual', 'disabled' - lookup = dict((name, number) for number, name in enumerate(types)) - return lookup[self.startuptype] + def infostartup(self): + self.isuphandle = win32api.RegOpenKeyEx( + win32con.HKEY_LOCAL_MACHINE, self.sccss + self.sserv, 0, win32con.KEY_READ + ) + self.isuptype = win32api.RegQueryValueEx(self.isuphandle, "Start")[0] + win32api.RegCloseKey(self.isuphandle) + if self.isuptype == 0: + return "boot" + elif self.isuptype == 1: + return "system" + elif self.isuptype == 2: + return "automatic" + elif self.isuptype == 3: + return "manual" + elif self.isuptype == 4: + return "disabled" - def setstartup(self, startuptype): - self.startuptype = startuptype.lower() - self.snc = win32service.SERVICE_NO_CHANGE - win32service.ChangeServiceConfig(self.handle, self.snc, self.suptype, - self.snc, None, None, 0, None, None, None, self.lserv) + @property + def suptype(self): + types = 'boot', 'system', 'automatic', 'manual', 'disabled' + lookup = dict((name, number) for number, name in enumerate(types)) + return lookup[self.startuptype] - def getname(self): - self.snames=win32service.EnumServicesStatus(self.scmhandle) - for i in self.snames: - if i[0].lower() == self.userv.lower(): - return i[0], i[1] - break - if i[1].lower() == self.userv.lower(): - return i[0], i[1] - break - print("Error: The %s service doesn't seem to exist." % self.userv) - return None, None + def setstartup(self, startuptype): + self.startuptype = startuptype.lower() + self.snc = win32service.SERVICE_NO_CHANGE + win32service.ChangeServiceConfig( + self.handle, + self.snc, + self.suptype, + self.snc, + None, + None, + 0, + None, + None, + None, + self.lserv, + ) + + def getname(self): + self.snames = win32service.EnumServicesStatus(self.scmhandle) + for i in self.snames: + if i[0].lower() == self.userv.lower(): + return i[0], i[1] + break + if i[1].lower() == self.userv.lower(): + return i[0], i[1] + break + print("Error: The %s service doesn't seem to exist." % self.userv) + return None, None diff --git a/libs/win/jaraco/windows/shell.py b/libs/win/jaraco/windows/shell.py index 58333359..2805cbe4 100644 --- a/libs/win/jaraco/windows/shell.py +++ b/libs/win/jaraco/windows/shell.py @@ -2,13 +2,13 @@ from .api import shell def get_recycle_bin_confirm(): - settings = shell.SHELLSTATE() - shell.SHGetSetSettings(settings, shell.SSF_NOCONFIRMRECYCLE, False) - return not settings.no_confirm_recycle + settings = shell.SHELLSTATE() + shell.SHGetSetSettings(settings, shell.SSF_NOCONFIRMRECYCLE, False) + return not settings.no_confirm_recycle def set_recycle_bin_confirm(confirm=False): - settings = shell.SHELLSTATE() - settings.no_confirm_recycle = not confirm - shell.SHGetSetSettings(settings, shell.SSF_NOCONFIRMRECYCLE, True) - # cross fingers and hope it worked + settings = shell.SHELLSTATE() + settings.no_confirm_recycle = not confirm + shell.SHGetSetSettings(settings, shell.SSF_NOCONFIRMRECYCLE, True) + # cross fingers and hope it worked diff --git a/libs/win/jaraco/windows/timers.py b/libs/win/jaraco/windows/timers.py index 626118a9..caf4a58e 100644 --- a/libs/win/jaraco/windows/timers.py +++ b/libs/win/jaraco/windows/timers.py @@ -1,71 +1,66 @@ -# -*- coding: UTF-8 -*- - """ timers - In particular, contains a waitable timer. + In particular, contains a waitable timer. """ -from __future__ import absolute_import - import time -from six.moves import _thread +import _thread from jaraco.windows.api import event as win32event -__author__ = 'Jason R. Coombs ' - class WaitableTimer: - """ - t = WaitableTimer() - t.set(None, 10) # every 10 seconds - t.wait_for_signal() # 10 seconds elapses - t.stop() - t.wait_for_signal(20) # 20 seconds elapses (timeout occurred) - """ - def __init__(self): - self.signal_event = win32event.CreateEvent(None, 0, 0, None) - self.stop_event = win32event.CreateEvent(None, 0, 0, None) + """ + t = WaitableTimer() + t.set(None, 10) # every 10 seconds + t.wait_for_signal() # 10 seconds elapses + t.stop() + t.wait_for_signal(20) # 20 seconds elapses (timeout occurred) + """ - def set(self, due_time, period): - _thread.start_new_thread(self._signal_loop, (due_time, period)) + def __init__(self): + self.signal_event = win32event.CreateEvent(None, 0, 0, None) + self.stop_event = win32event.CreateEvent(None, 0, 0, None) - def stop(self): - win32event.SetEvent(self.stop_event) + def set(self, due_time, period): + _thread.start_new_thread(self._signal_loop, (due_time, period)) - def wait_for_signal(self, timeout=None): - """ - wait for the signal; return after the signal has occurred or the - timeout in seconds elapses. - """ - timeout_ms = int(timeout * 1000) if timeout else win32event.INFINITE - win32event.WaitForSingleObject(self.signal_event, timeout_ms) + def stop(self): + win32event.SetEvent(self.stop_event) - def _signal_loop(self, due_time, period): - if not due_time and not period: - raise ValueError("due_time or period must be non-zero") - try: - if not due_time: - due_time = time.time() + period - if due_time: - self._wait(due_time - time.time()) - while period: - due_time += period - self._wait(due_time - time.time()) - except Exception: - pass + def wait_for_signal(self, timeout=None): + """ + wait for the signal; return after the signal has occurred or the + timeout in seconds elapses. + """ + timeout_ms = int(timeout * 1000) if timeout else win32event.INFINITE + win32event.WaitForSingleObject(self.signal_event, timeout_ms) - def _wait(self, seconds): - milliseconds = int(seconds * 1000) - if milliseconds > 0: - res = win32event.WaitForSingleObject(self.stop_event, milliseconds) - if res == win32event.WAIT_OBJECT_0: - raise Exception - if res == win32event.WAIT_TIMEOUT: - pass - win32event.SetEvent(self.signal_event) + def _signal_loop(self, due_time, period): + if not due_time and not period: + raise ValueError("due_time or period must be non-zero") + try: + if not due_time: + due_time = time.time() + period + if due_time: + self._wait(due_time - time.time()) + while period: + due_time += period + self._wait(due_time - time.time()) + except Exception: + pass - @staticmethod - def get_even_due_time(period): - now = time.time() - return now - (now % period) + def _wait(self, seconds): + milliseconds = int(seconds * 1000) + if milliseconds > 0: + res = win32event.WaitForSingleObject(self.stop_event, milliseconds) + if res == win32event.WAIT_OBJECT_0: + raise Exception + if res == win32event.WAIT_TIMEOUT: + pass + win32event.SetEvent(self.signal_event) + + @staticmethod + def get_even_due_time(period): + now = time.time() + return now - (now % period) diff --git a/libs/win/jaraco/windows/timezone.py b/libs/win/jaraco/windows/timezone.py index 7eedcf0b..fdefc931 100644 --- a/libs/win/jaraco/windows/timezone.py +++ b/libs/win/jaraco/windows/timezone.py @@ -10,245 +10,253 @@ from jaraco.collections import RangeMap class AnyDict(object): - "A dictionary that returns the same value regardless of key" + "A dictionary that returns the same value regardless of key" - def __init__(self, value): - self.value = value + def __init__(self, value): + self.value = value - def __getitem__(self, key): - return self.value + def __getitem__(self, key): + return self.value class SYSTEMTIME(Extended, ctypes.Structure): - _fields_ = [ - ('year', WORD), - ('month', WORD), - ('day_of_week', WORD), - ('day', WORD), - ('hour', WORD), - ('minute', WORD), - ('second', WORD), - ('millisecond', WORD), - ] + _fields_ = [ + ('year', WORD), + ('month', WORD), + ('day_of_week', WORD), + ('day', WORD), + ('hour', WORD), + ('minute', WORD), + ('second', WORD), + ('millisecond', WORD), + ] class REG_TZI_FORMAT(Extended, ctypes.Structure): - _fields_ = [ - ('bias', LONG), - ('standard_bias', LONG), - ('daylight_bias', LONG), - ('standard_start', SYSTEMTIME), - ('daylight_start', SYSTEMTIME), - ] + _fields_ = [ + ('bias', LONG), + ('standard_bias', LONG), + ('daylight_bias', LONG), + ('standard_start', SYSTEMTIME), + ('daylight_start', SYSTEMTIME), + ] class TIME_ZONE_INFORMATION(Extended, ctypes.Structure): - _fields_ = [ - ('bias', LONG), - ('standard_name', WCHAR * 32), - ('standard_start', SYSTEMTIME), - ('standard_bias', LONG), - ('daylight_name', WCHAR * 32), - ('daylight_start', SYSTEMTIME), - ('daylight_bias', LONG), - ] + _fields_ = [ + ('bias', LONG), + ('standard_name', WCHAR * 32), + ('standard_start', SYSTEMTIME), + ('standard_bias', LONG), + ('daylight_name', WCHAR * 32), + ('daylight_start', SYSTEMTIME), + ('daylight_bias', LONG), + ] class DYNAMIC_TIME_ZONE_INFORMATION(TIME_ZONE_INFORMATION): - """ - Because the structure of the DYNAMIC_TIME_ZONE_INFORMATION extends - the structure of the TIME_ZONE_INFORMATION, this structure - can be used as a drop-in replacement for calls where the - structure is passed by reference. + """ + Because the structure of the DYNAMIC_TIME_ZONE_INFORMATION extends + the structure of the TIME_ZONE_INFORMATION, this structure + can be used as a drop-in replacement for calls where the + structure is passed by reference. - For example, - dynamic_tzi = DYNAMIC_TIME_ZONE_INFORMATION() - ctypes.windll.kernel32.GetTimeZoneInformation(ctypes.byref(dynamic_tzi)) + For example, + dynamic_tzi = DYNAMIC_TIME_ZONE_INFORMATION() + ctypes.windll.kernel32.GetTimeZoneInformation(ctypes.byref(dynamic_tzi)) - (although the key_name and dynamic_daylight_time_disabled flags will be - set to the default (null)). + (although the key_name and dynamic_daylight_time_disabled flags will be + set to the default (null)). - >>> isinstance(DYNAMIC_TIME_ZONE_INFORMATION(), TIME_ZONE_INFORMATION) - True + >>> isinstance(DYNAMIC_TIME_ZONE_INFORMATION(), TIME_ZONE_INFORMATION) + True - """ - _fields_ = [ - # ctypes automatically includes the fields from the parent - ('key_name', WCHAR * 128), - ('dynamic_daylight_time_disabled', BOOL), - ] + """ - def __init__(self, *args, **kwargs): - """Allow initialization from args from both this class and - its superclass. Default ctypes implementation seems to - assume that this class is only initialized with its own - _fields_ (for non-keyword-args).""" - super_self = super(DYNAMIC_TIME_ZONE_INFORMATION, self) - super_fields = super_self._fields_ - super_args = args[:len(super_fields)] - self_args = args[len(super_fields):] - # convert the super args to keyword args so they're also handled - for field, arg in zip(super_fields, super_args): - field_name, spec = field - kwargs[field_name] = arg - super(DYNAMIC_TIME_ZONE_INFORMATION, self).__init__(*self_args, **kwargs) + _fields_ = [ + # ctypes automatically includes the fields from the parent + ('key_name', WCHAR * 128), + ('dynamic_daylight_time_disabled', BOOL), + ] + + def __init__(self, *args, **kwargs): + """Allow initialization from args from both this class and + its superclass. Default ctypes implementation seems to + assume that this class is only initialized with its own + _fields_ (for non-keyword-args).""" + super_self = super(DYNAMIC_TIME_ZONE_INFORMATION, self) + super_fields = super_self._fields_ + super_args = args[: len(super_fields)] + self_args = args[len(super_fields) :] + # convert the super args to keyword args so they're also handled + for field, arg in zip(super_fields, super_args): + field_name, spec = field + kwargs[field_name] = arg + super(DYNAMIC_TIME_ZONE_INFORMATION, self).__init__(*self_args, **kwargs) class Info(DYNAMIC_TIME_ZONE_INFORMATION): - """ - A time zone definition class based on the win32 - DYNAMIC_TIME_ZONE_INFORMATION structure. + """ + A time zone definition class based on the win32 + DYNAMIC_TIME_ZONE_INFORMATION structure. - Describes a bias against UTC (bias), and two dates at which a separate - additional bias applies (standard_bias and daylight_bias). - """ + Describes a bias against UTC (bias), and two dates at which a separate + additional bias applies (standard_bias and daylight_bias). + """ - def field_names(self): - return map(operator.itemgetter(0), self._fields_) + def field_names(self): + return map(operator.itemgetter(0), self._fields_) - def __init__(self, *args, **kwargs): - """ - Try to construct a timezone.Info from - a) [DYNAMIC_]TIME_ZONE_INFORMATION args - b) another Info - c) a REG_TZI_FORMAT - d) a byte structure - """ - funcs = ( - super(Info, self).__init__, - self.__init_from_other, - self.__init_from_reg_tzi, - self.__init_from_bytes, - ) - for func in funcs: - try: - func(*args, **kwargs) - return - except TypeError: - pass - raise TypeError("Invalid arguments for %s" % self.__class__) + def __init__(self, *args, **kwargs): + """ + Try to construct a timezone.Info from + a) [DYNAMIC_]TIME_ZONE_INFORMATION args + b) another Info + c) a REG_TZI_FORMAT + d) a byte structure + """ + funcs = ( + super(Info, self).__init__, + self.__init_from_other, + self.__init_from_reg_tzi, + self.__init_from_bytes, + ) + for func in funcs: + try: + func(*args, **kwargs) + return + except TypeError: + pass + raise TypeError("Invalid arguments for %s" % self.__class__) - def __init_from_bytes(self, bytes, **kwargs): - reg_tzi = REG_TZI_FORMAT() - # todo: use buffer API in Python 3 - buffer = memoryview(bytes) - ctypes.memmove(ctypes.addressof(reg_tzi), buffer, len(buffer)) - self.__init_from_reg_tzi(self, reg_tzi, **kwargs) + def __init_from_bytes(self, bytes, **kwargs): + reg_tzi = REG_TZI_FORMAT() + # todo: use buffer API in Python 3 + buffer = memoryview(bytes) + ctypes.memmove(ctypes.addressof(reg_tzi), buffer, len(buffer)) + self.__init_from_reg_tzi(self, reg_tzi, **kwargs) - def __init_from_reg_tzi(self, reg_tzi, **kwargs): - if not isinstance(reg_tzi, REG_TZI_FORMAT): - raise TypeError("Not a REG_TZI_FORMAT") - for field_name, type in reg_tzi._fields_: - setattr(self, field_name, getattr(reg_tzi, field_name)) - for name, value in kwargs.items(): - setattr(self, name, value) + def __init_from_reg_tzi(self, reg_tzi, **kwargs): + if not isinstance(reg_tzi, REG_TZI_FORMAT): + raise TypeError("Not a REG_TZI_FORMAT") + for field_name, type in reg_tzi._fields_: + setattr(self, field_name, getattr(reg_tzi, field_name)) + for name, value in kwargs.items(): + setattr(self, name, value) - def __init_from_other(self, other): - if not isinstance(other, TIME_ZONE_INFORMATION): - raise TypeError("Not a TIME_ZONE_INFORMATION") - for name in other.field_names(): - # explicitly get the value from the underlying structure - value = super(Info, other).__getattribute__(other, name) - setattr(self, name, value) - # consider instead of the loop above just copying the memory directly - # size = max(ctypes.sizeof(DYNAMIC_TIME_ZONE_INFO), ctypes.sizeof(other)) - # ctypes.memmove(ctypes.addressof(self), other, size) + def __init_from_other(self, other): + if not isinstance(other, TIME_ZONE_INFORMATION): + raise TypeError("Not a TIME_ZONE_INFORMATION") + for name in other.field_names(): + # explicitly get the value from the underlying structure + value = super(Info, other).__getattribute__(other, name) + setattr(self, name, value) + # consider instead of the loop above just copying the memory directly + # size = max(ctypes.sizeof(DYNAMIC_TIME_ZONE_INFO), ctypes.sizeof(other)) + # ctypes.memmove(ctypes.addressof(self), other, size) - def __getattribute__(self, attr): - value = super(Info, self).__getattribute__(attr) + def __getattribute__(self, attr): + value = super(Info, self).__getattribute__(attr) - def make_minute_timedelta(m): - datetime.timedelta(minutes=m) - if 'bias' in attr: - value = make_minute_timedelta(value) - return value + def make_minute_timedelta(m): + datetime.timedelta(minutes=m) - @classmethod - def current(class_): - "Windows Platform SDK GetTimeZoneInformation" - tzi = class_() - kernel32 = ctypes.windll.kernel32 - getter = kernel32.GetTimeZoneInformation - getter = getattr(kernel32, 'GetDynamicTimeZoneInformation', getter) - code = getter(ctypes.byref(tzi)) - return code, tzi + if 'bias' in attr: + value = make_minute_timedelta(value) + return value - def set(self): - kernel32 = ctypes.windll.kernel32 - setter = kernel32.SetTimeZoneInformation - setter = getattr(kernel32, 'SetDynamicTimeZoneInformation', setter) - return setter(ctypes.byref(self)) + @classmethod + def current(class_): + "Windows Platform SDK GetTimeZoneInformation" + tzi = class_() + kernel32 = ctypes.windll.kernel32 + getter = kernel32.GetTimeZoneInformation + getter = getattr(kernel32, 'GetDynamicTimeZoneInformation', getter) + code = getter(ctypes.byref(tzi)) + return code, tzi - def copy(self): - return self.__class__(self) + def set(self): + kernel32 = ctypes.windll.kernel32 + setter = kernel32.SetTimeZoneInformation + setter = getattr(kernel32, 'SetDynamicTimeZoneInformation', setter) + return setter(ctypes.byref(self)) - def locate_daylight_start(self, year): - info = self.get_info_for_year(year) - return self._locate_day(year, info.daylight_start) + def copy(self): + return self.__class__(self) - def locate_standard_start(self, year): - info = self.get_info_for_year(year) - return self._locate_day(year, info.standard_start) + def locate_daylight_start(self, year): + info = self.get_info_for_year(year) + return self._locate_day(year, info.daylight_start) - def get_info_for_year(self, year): - return self.dynamic_info[year] + def locate_standard_start(self, year): + info = self.get_info_for_year(year) + return self._locate_day(year, info.standard_start) - @property - def dynamic_info(self): - "Return a map that for a given year will return the correct Info" - if self.key_name: - dyn_key = self.get_key().subkey('Dynamic DST') - del dyn_key['FirstEntry'] - del dyn_key['LastEntry'] - years = map(int, dyn_key.keys()) - values = map(Info, dyn_key.values()) - # create a range mapping that searches by descending year and matches - # if the target year is greater or equal. - return RangeMap(zip(years, values), RangeMap.descending, operator.ge) - else: - return AnyDict(self) + def get_info_for_year(self, year): + return self.dynamic_info[year] - @staticmethod - def _locate_day(year, cutoff): - """ - Takes a SYSTEMTIME object, such as retrieved from a TIME_ZONE_INFORMATION - structure or call to GetTimeZoneInformation and interprets - it based on the given - year to identify the actual day. + @property + def dynamic_info(self): + "Return a map that for a given year will return the correct Info" + if self.key_name: + dyn_key = self.get_key().subkey('Dynamic DST') + del dyn_key['FirstEntry'] + del dyn_key['LastEntry'] + years = map(int, dyn_key.keys()) + values = map(Info, dyn_key.values()) + # create a range mapping that searches by descending year and matches + # if the target year is greater or equal. + return RangeMap(zip(years, values), RangeMap.descending, operator.ge) + else: + return AnyDict(self) - This method is necessary because the SYSTEMTIME structure - refers to a day by its - day of the week and week of the month (e.g. 4th saturday in March). + @staticmethod + def _locate_day(year, cutoff): + """ + Takes a SYSTEMTIME object, such as retrieved from a TIME_ZONE_INFORMATION + structure or call to GetTimeZoneInformation and interprets + it based on the given + year to identify the actual day. - >>> SATURDAY = 6 - >>> MARCH = 3 - >>> st = SYSTEMTIME(2000, MARCH, SATURDAY, 4, 0, 0, 0, 0) + This method is necessary because the SYSTEMTIME structure + refers to a day by its + day of the week and week of the month (e.g. 4th saturday in March). - # according to my calendar, the 4th Saturday in March in 2009 was the 28th - >>> expected_date = datetime.datetime(2009, 3, 28) - >>> Info._locate_day(2009, st) == expected_date - True - """ - # MS stores Sunday as 0, Python datetime stores Monday as zero - target_weekday = (cutoff.day_of_week + 6) % 7 - # For SYSTEMTIMEs relating to time zone inforamtion, cutoff.day - # is the week of the month - week_of_month = cutoff.day - # so the following is the first day of that week - day = (week_of_month - 1) * 7 + 1 - result = datetime.datetime( - year, cutoff.month, day, - cutoff.hour, cutoff.minute, cutoff.second, cutoff.millisecond) - # now the result is the correct week, but not necessarily - # the correct day of the week - days_to_go = (target_weekday - result.weekday()) % 7 - result += datetime.timedelta(days_to_go) - # if we selected a day in the month following the target month, - # move back a week or two. - # This is necessary because Microsoft defines the fifth week in a month - # to be the last week in a month and adding the time delta might have - # pushed the result into the next month. - while result.month == cutoff.month + 1: - result -= datetime.timedelta(weeks=1) - return result + >>> SATURDAY = 6 + >>> MARCH = 3 + >>> st = SYSTEMTIME(2000, MARCH, SATURDAY, 4, 0, 0, 0, 0) + + # according to my calendar, the 4th Saturday in March in 2009 was the 28th + >>> expected_date = datetime.datetime(2009, 3, 28) + >>> Info._locate_day(2009, st) == expected_date + True + """ + # MS stores Sunday as 0, Python datetime stores Monday as zero + target_weekday = (cutoff.day_of_week + 6) % 7 + # For SYSTEMTIMEs relating to time zone inforamtion, cutoff.day + # is the week of the month + week_of_month = cutoff.day + # so the following is the first day of that week + day = (week_of_month - 1) * 7 + 1 + result = datetime.datetime( + year, + cutoff.month, + day, + cutoff.hour, + cutoff.minute, + cutoff.second, + cutoff.millisecond, + ) + # now the result is the correct week, but not necessarily + # the correct day of the week + days_to_go = (target_weekday - result.weekday()) % 7 + result += datetime.timedelta(days_to_go) + # if we selected a day in the month following the target month, + # move back a week or two. + # This is necessary because Microsoft defines the fifth week in a month + # to be the last week in a month and adding the time delta might have + # pushed the result into the next month. + while result.month == cutoff.month + 1: + result -= datetime.timedelta(weeks=1) + return result diff --git a/libs/win/jaraco/windows/ui.py b/libs/win/jaraco/windows/ui.py index 20f948f3..7249331e 100644 --- a/libs/win/jaraco/windows/ui.py +++ b/libs/win/jaraco/windows/ui.py @@ -5,5 +5,5 @@ from jaraco.windows.util import ensure_unicode def MessageBox(text, caption=None, handle=None, type=None): - text, caption = map(ensure_unicode, (text, caption)) - ctypes.windll.user32.MessageBoxW(handle, text, caption, type) + text, caption = map(ensure_unicode, (text, caption)) + ctypes.windll.user32.MessageBoxW(handle, text, caption, type) diff --git a/libs/win/jaraco/windows/user.py b/libs/win/jaraco/windows/user.py index 9b574777..503233b7 100644 --- a/libs/win/jaraco/windows/user.py +++ b/libs/win/jaraco/windows/user.py @@ -5,12 +5,12 @@ from .error import WindowsError, handle_nonzero_success def get_user_name(): - size = ctypes.wintypes.DWORD() - try: - handle_nonzero_success(GetUserName(None, size)) - except WindowsError as e: - if e.code != errors.ERROR_INSUFFICIENT_BUFFER: - raise - buffer = ctypes.create_unicode_buffer(size.value) - handle_nonzero_success(GetUserName(buffer, size)) - return buffer.value + size = ctypes.wintypes.DWORD() + try: + handle_nonzero_success(GetUserName(None, size)) + except WindowsError as e: + if e.code != errors.ERROR_INSUFFICIENT_BUFFER: + raise + buffer = ctypes.create_unicode_buffer(size.value) + handle_nonzero_success(GetUserName(buffer, size)) + return buffer.value diff --git a/libs/win/jaraco/windows/util.py b/libs/win/jaraco/windows/util.py index 5524df85..c51ff997 100644 --- a/libs/win/jaraco/windows/util.py +++ b/libs/win/jaraco/windows/util.py @@ -4,17 +4,18 @@ import ctypes def ensure_unicode(param): - try: - param = ctypes.create_unicode_buffer(param) - except TypeError: - pass # just return the param as is - return param + try: + param = ctypes.create_unicode_buffer(param) + except TypeError: + pass # just return the param as is + return param class Extended(object): - "Used to add extended capability to structures" - def __eq__(self, other): - return memoryview(self) == memoryview(other) + "Used to add extended capability to structures" - def __ne__(self, other): - return memoryview(self) != memoryview(other) + def __eq__(self, other): + return memoryview(self) == memoryview(other) + + def __ne__(self, other): + return memoryview(self) != memoryview(other) diff --git a/libs/win/jaraco/windows/vpn.py b/libs/win/jaraco/windows/vpn.py index 9cf31dc1..df9f1503 100644 --- a/libs/win/jaraco/windows/vpn.py +++ b/libs/win/jaraco/windows/vpn.py @@ -3,15 +3,19 @@ from path import Path def install_pptp(name, param_lines): - """ - """ - # or consider using the API: - # http://msdn.microsoft.com/en-us/library/aa446739%28v=VS.85%29.aspx - pbk_path = ( - Path(os.environ['PROGRAMDATA']) - / 'Microsoft' / 'Network' / 'Connections' / 'pbk' / 'rasphone.pbk') - pbk_path.dirname().makedirs_p() - with open(pbk_path, 'a') as pbk: - pbk.write('[{name}]\n'.format(name=name)) - pbk.writelines(param_lines) - pbk.write('\n') + """ """ + # or consider using the API: + # http://msdn.microsoft.com/en-us/library/aa446739%28v=VS.85%29.aspx + pbk_path = ( + Path(os.environ['PROGRAMDATA']) + / 'Microsoft' + / 'Network' + / 'Connections' + / 'pbk' + / 'rasphone.pbk' + ) + pbk_path.dirname().makedirs_p() + with open(pbk_path, 'a') as pbk: + pbk.write('[{name}]\n'.format(name=name)) + pbk.writelines(param_lines) + pbk.write('\n') diff --git a/libs/win/jaraco/windows/xmouse.py b/libs/win/jaraco/windows/xmouse.py index 20b19435..8f3485f3 100644 --- a/libs/win/jaraco/windows/xmouse.py +++ b/libs/win/jaraco/windows/xmouse.py @@ -1,7 +1,3 @@ -#!python - -from __future__ import print_function - import ctypes from jaraco.windows.error import handle_nonzero_success from jaraco.windows.api import system @@ -9,92 +5,84 @@ from jaraco.ui.cmdline import Command def set(value): - result = system.SystemParametersInfo( - system.SPI_SETACTIVEWINDOWTRACKING, - 0, - ctypes.cast(value, ctypes.c_void_p), - 0, - ) - handle_nonzero_success(result) + result = system.SystemParametersInfo( + system.SPI_SETACTIVEWINDOWTRACKING, 0, ctypes.cast(value, ctypes.c_void_p), 0 + ) + handle_nonzero_success(result) def get(): - value = ctypes.wintypes.BOOL() - result = system.SystemParametersInfo( - system.SPI_GETACTIVEWINDOWTRACKING, - 0, - ctypes.byref(value), - 0, - ) - handle_nonzero_success(result) - return bool(value) + value = ctypes.wintypes.BOOL() + result = system.SystemParametersInfo( + system.SPI_GETACTIVEWINDOWTRACKING, 0, ctypes.byref(value), 0 + ) + handle_nonzero_success(result) + return bool(value) def set_delay(milliseconds): - result = system.SystemParametersInfo( - system.SPI_SETACTIVEWNDTRKTIMEOUT, - 0, - ctypes.cast(milliseconds, ctypes.c_void_p), - 0, - ) - handle_nonzero_success(result) + result = system.SystemParametersInfo( + system.SPI_SETACTIVEWNDTRKTIMEOUT, + 0, + ctypes.cast(milliseconds, ctypes.c_void_p), + 0, + ) + handle_nonzero_success(result) def get_delay(): - value = ctypes.wintypes.DWORD() - result = system.SystemParametersInfo( - system.SPI_GETACTIVEWNDTRKTIMEOUT, - 0, - ctypes.byref(value), - 0, - ) - handle_nonzero_success(result) - return int(value.value) + value = ctypes.wintypes.DWORD() + result = system.SystemParametersInfo( + system.SPI_GETACTIVEWNDTRKTIMEOUT, 0, ctypes.byref(value), 0 + ) + handle_nonzero_success(result) + return int(value.value) class DelayParam(Command): - @staticmethod - def add_arguments(parser): - parser.add_argument( - '-d', '--delay', type=int, - help="Delay in milliseconds for active window tracking" - ) + @staticmethod + def add_arguments(parser): + parser.add_argument( + '-d', + '--delay', + type=int, + help="Delay in milliseconds for active window tracking", + ) class Show(Command): - @classmethod - def run(cls, args): - msg = "xmouse: {enabled} (delay {delay}ms)".format( - enabled=get(), - delay=get_delay(), - ) - print(msg) + @classmethod + def run(cls, args): + msg = "xmouse: {enabled} (delay {delay}ms)".format( + enabled=get(), delay=get_delay() + ) + print(msg) class Enable(DelayParam): - @classmethod - def run(cls, args): - print("enabling xmouse") - set(True) - args.delay and set_delay(args.delay) + @classmethod + def run(cls, args): + print("enabling xmouse") + set(True) + args.delay and set_delay(args.delay) class Disable(DelayParam): - @classmethod - def run(cls, args): - print("disabling xmouse") - set(False) - args.delay and set_delay(args.delay) + @classmethod + def run(cls, args): + print("disabling xmouse") + set(False) + args.delay and set_delay(args.delay) class Toggle(DelayParam): - @classmethod - def run(cls, args): - value = get() - print("xmouse: %s -> %s" % (value, not value)) - set(not value) - args.delay and set_delay(args.delay) + @classmethod + def run(cls, args): + value = get() + print("xmouse: %s -> %s" % (value, not value)) + set(not value) + args.delay and set_delay(args.delay) if __name__ == '__main__': - Command.invoke() + Command.invoke() diff --git a/libs/win/more_itertools/__init__.py b/libs/win/more_itertools/__init__.py index bba462c3..557bfc20 100644 --- a/libs/win/more_itertools/__init__.py +++ b/libs/win/more_itertools/__init__.py @@ -1,2 +1,6 @@ -from more_itertools.more import * # noqa -from more_itertools.recipes import * # noqa +"""More routines for operating on iterables, beyond itertools""" + +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '9.0.0' diff --git a/libs/win/more_itertools/__init__.pyi b/libs/win/more_itertools/__init__.pyi new file mode 100644 index 00000000..96f6e36c --- /dev/null +++ b/libs/win/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/libs/win/more_itertools/more.py b/libs/win/more_itertools/more.py index 05e851ee..7f73d6ed 100644 --- a/libs/win/more_itertools/more.py +++ b/libs/win/more_itertools/more.py @@ -1,8 +1,9 @@ -from __future__ import print_function +import warnings -from collections import Counter, defaultdict, deque -from functools import partial, wraps -from heapq import merge +from collections import Counter, defaultdict, deque, abc +from collections.abc import Sequence +from functools import partial, reduce, wraps +from heapq import heapify, heapreplace, heappop from itertools import ( chain, compress, @@ -14,100 +15,162 @@ from itertools import ( repeat, starmap, takewhile, - tee + tee, + zip_longest, ) -from operator import itemgetter, lt, gt, sub -from sys import maxsize, version_info -try: - from collections.abc import Sequence -except ImportError: - from collections import Sequence +from math import exp, factorial, floor, log +from queue import Empty, Queue +from random import random, randrange, uniform +from operator import itemgetter, mul, sub, gt, lt, ge, le +from sys import hexversion, maxsize +from time import monotonic -from six import binary_type, string_types, text_type -from six.moves import filter, map, range, zip, zip_longest - -from .recipes import consume, flatten, take +from .recipes import ( + _marker, + _zip_equal, + UnequalIterablesError, + consume, + flatten, + pairwise, + powerset, + take, + unique_everseen, + all_equal, +) __all__ = [ + 'AbortThread', + 'SequenceView', + 'UnequalIterablesError', 'adjacent', + 'all_unique', 'always_iterable', 'always_reversible', 'bucket', + 'callback_iter', 'chunked', + 'chunked_even', 'circular_shifts', 'collapse', - 'collate', + 'combination_index', 'consecutive_groups', + 'constrained_batches', 'consumer', 'count_cycle', + 'countable', 'difference', + 'distinct_combinations', 'distinct_permutations', 'distribute', 'divide', + 'duplicates_everseen', + 'duplicates_justseen', 'exactly_n', + 'filter_except', 'first', 'groupby_transform', + 'ichunked', + 'iequals', 'ilen', - 'interleave_longest', 'interleave', + 'interleave_evenly', + 'interleave_longest', 'intersperse', + 'is_sorted', 'islice_extended', 'iterate', 'last', 'locate', + 'longest_common_prefix', 'lstrip', 'make_decorator', + 'map_except', + 'map_if', 'map_reduce', + 'mark_ends', + 'minmax', + 'nth_or_last', + 'nth_permutation', + 'nth_product', 'numeric_range', 'one', + 'only', 'padded', + 'partitions', 'peekable', + 'permutation_index', + 'product_index', + 'raise_', + 'repeat_each', + 'repeat_last', 'replace', 'rlocate', 'rstrip', 'run_length', + 'sample', 'seekable', - 'SequenceView', + 'set_partitions', 'side_effect', 'sliced', 'sort_together', - 'split_at', 'split_after', + 'split_at', 'split_before', + 'split_into', + 'split_when', 'spy', 'stagger', 'strip', + 'strictly_n', + 'substrings', + 'substrings_indexes', + 'time_limited', + 'unique_in_window', 'unique_to_each', + 'unzip', + 'value_chain', 'windowed', + 'windowed_complete', 'with_iter', + 'zip_broadcast', + 'zip_equal', 'zip_offset', ] -_marker = object() - -def chunked(iterable, n): +def chunked(iterable, n, strict=False): """Break *iterable* into lists of length *n*: >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) [[1, 2, 3], [4, 5, 6]] - If the length of *iterable* is not evenly divisible by *n*, the last - returned list will be shorter: + By the default, the last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) [[1, 2, 3], [4, 5, 6], [7, 8]] To use a fill-in value instead, see the :func:`grouper` recipe. - :func:`chunked` is useful for splitting up a computation on a large number - of keys into batches, to be pickled and sent off to worker processes. One - example is operations on rows in MySQL, which does not implement - server-side cursors properly and would otherwise load the entire dataset - into RAM on the client. + If the length of *iterable* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + list is yielded. """ - return iter(partial(take, n, iter(iterable)), []) + iterator = iter(partial(take, n, iter(iterable)), []) + if strict: + if n is None: + raise ValueError('n must not be None when using strict mode.') + + def ret(): + for chunk in iterator: + if len(chunk) != n: + raise ValueError('iterable is not divisible by n.') + yield chunk + + return iter(ret()) + else: + return iterator def first(iterable, default=_marker): @@ -129,14 +192,12 @@ def first(iterable, default=_marker): """ try: return next(iter(iterable)) - except StopIteration: - # I'm on the edge about raising ValueError instead of StopIteration. At - # the moment, ValueError wins, because the caller could conceivably - # want to do something different with flow control when I raise the - # exception, and it's weird to explicitly catch StopIteration. + except StopIteration as e: if default is _marker: - raise ValueError('first() was called on an empty iterable, and no ' - 'default value was provided.') + raise ValueError( + 'first() was called on an empty iterable, and no ' + 'default value was provided.' + ) from e return default @@ -153,20 +214,40 @@ def last(iterable, default=_marker): raise ``ValueError``. """ try: - try: - # Try to access the last item directly + if isinstance(iterable, Sequence): return iterable[-1] - except (TypeError, AttributeError, KeyError): - # If not slice-able, iterate entirely using length-1 deque - return deque(iterable, maxlen=1)[0] - except IndexError: # If the iterable was empty + # Work around https://bugs.python.org/issue38525 + elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): + return next(reversed(iterable)) + else: + return deque(iterable, maxlen=1)[-1] + except (IndexError, TypeError, StopIteration): if default is _marker: - raise ValueError('last() was called on an empty iterable, and no ' - 'default value was provided.') + raise ValueError( + 'last() was called on an empty iterable, and no default was ' + 'provided.' + ) return default -class peekable(object): +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: """Wrap an iterator to allow lookahead and prepending elements. Call :meth:`peek` on the result to get the value that will be returned @@ -219,11 +300,12 @@ class peekable(object): >>> if p: # peekable has items ... list(p) ['a', 'b'] - >>> if not p: # peekable is exhaused + >>> if not p: # peekable is exhausted ... list(p) [] """ + def __init__(self, iterable): self._it = iter(iterable) self._cache = deque() @@ -238,10 +320,6 @@ class peekable(object): return False return True - def __nonzero__(self): - # For Python 2 compatibility - return self.__bool__() - def peek(self, default=_marker): """Return the item that will be next returned from ``next()``. @@ -295,8 +373,6 @@ class peekable(object): return next(self._it) - next = __next__ # For Python 2 compatibility - def _get_slice(self, index): # Normalize the slice's arguments step = 1 if (index.step is None) else index.step @@ -336,70 +412,6 @@ class peekable(object): return self._cache[index] -def _collate(*iterables, **kwargs): - """Helper for ``collate()``, called when the user is using the ``reverse`` - or ``key`` keyword arguments on Python versions below 3.5. - - """ - key = kwargs.pop('key', lambda a: a) - reverse = kwargs.pop('reverse', False) - - min_or_max = partial(max if reverse else min, key=itemgetter(0)) - peekables = [peekable(it) for it in iterables] - peekables = [p for p in peekables if p] # Kill empties. - while peekables: - _, p = min_or_max((key(p.peek()), p) for p in peekables) - yield next(p) - peekables = [x for x in peekables if x] - - -def collate(*iterables, **kwargs): - """Return a sorted merge of the items from each of several already-sorted - *iterables*. - - >>> list(collate('ACDZ', 'AZ', 'JKL')) - ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] - - Works lazily, keeping only the next value from each iterable in memory. Use - :func:`collate` to, for example, perform a n-way mergesort of items that - don't fit in memory. - - If a *key* function is specified, the iterables will be sorted according - to its result: - - >>> key = lambda s: int(s) # Sort by numeric value, not by string - >>> list(collate(['1', '10'], ['2', '11'], key=key)) - ['1', '2', '10', '11'] - - - If the *iterables* are sorted in descending order, set *reverse* to - ``True``: - - >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) - [5, 4, 3, 2, 1, 0] - - If the elements of the passed-in iterables are out of order, you might get - unexpected results. - - On Python 2.7, this function delegates to :func:`heapq.merge` if neither - of the keyword arguments are specified. On Python 3.5+, this function - is an alias for :func:`heapq.merge`. - - """ - if not kwargs: - return merge(*iterables) - - return _collate(*iterables, **kwargs) - - -# If using Python version 3.5 or greater, heapq.merge() will be faster than -# collate - use that instead. -if version_info >= (3, 5, 0): - _collate_docstring = collate.__doc__ - collate = partial(merge) - collate.__doc__ = _collate_docstring - - def consumer(func): """Decorator that automatically advances a PEP-342-style "reverse iterator" to its first yield point so you don't have to call ``next()`` on it @@ -422,11 +434,13 @@ def consumer(func): ``t.send()`` could be used. """ + @wraps(func) def wrapper(*args, **kwargs): gen = func(*args, **kwargs) next(gen) return gen + return wrapper @@ -439,20 +453,20 @@ def ilen(iterable): This consumes the iterable, so handle with care. """ - # maxlen=1 only stores the last item in the deque - d = deque(enumerate(iterable, 1), maxlen=1) - # since we started enumerate at 1, - # the first item of the last pair will be the length of the iterable - # (assuming there were items) - return d[0][0] if d else 0 + # This approach was selected because benchmarks showed it's likely the + # fastest of the known implementations at the time of writing. + # See GitHub tracker: #236, #230. + counter = count() + deque(zip(iterable, counter), maxlen=0) + return next(counter) def iterate(func, start): """Return ``start``, ``func(start)``, ``func(func(start))``, ... - >>> from itertools import islice - >>> list(islice(iterate(lambda x: 2*x, 1), 10)) - [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + >>> from itertools import islice + >>> list(islice(iterate(lambda x: 2*x, 1), 10)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] """ while True: @@ -472,8 +486,7 @@ def with_iter(context_manager): """ with context_manager as iterable: - for item in iterable: - yield item + yield from iterable def one(iterable, too_short=None, too_long=None): @@ -507,7 +520,8 @@ def one(iterable, too_short=None, too_long=None): >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... - ValueError: too many items in iterable (expected 1)' + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. >>> too_long = RuntimeError >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): @@ -515,29 +529,112 @@ def one(iterable, too_short=None, too_long=None): RuntimeError Note that :func:`one` attempts to advance *iterable* twice to ensure there - is only one item. If there is more than one, both items will be discarded. - See :func:`spy` or :func:`peekable` to check iterable contents less - destructively. + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. """ it = iter(iterable) try: - value = next(it) + first_value = next(it) + except StopIteration as e: + raise ( + too_short or ValueError('too few items in iterable (expected 1)') + ) from e + + try: + second_value = next(it) except StopIteration: - raise too_short or ValueError('too few items in iterable (expected 1)') + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def raise_(exception, *args): + raise exception(*args) + + +def strictly_n(iterable, n, too_short=None, too_long=None): + """Validate that *iterable* has exactly *n* items and return them if + it does. If it has fewer than *n* items, call function *too_short* + with those items. If it has more than *n* items, call function + *too_long* with the first ``n + 1`` items. + + >>> iterable = ['a', 'b', 'c', 'd'] + >>> n = 4 + >>> list(strictly_n(iterable, n)) + ['a', 'b', 'c', 'd'] + + By default, *too_short* and *too_long* are functions that raise + ``ValueError``. + + >>> list(strictly_n('ab', 3)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too few items in iterable (got 2) + + >>> list(strictly_n('abc', 2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (got at least 3) + + You can instead supply functions that do something else. + *too_short* will be called with the number of items in *iterable*. + *too_long* will be called with `n + 1`. + + >>> def too_short(item_count): + ... raise RuntimeError + >>> it = strictly_n('abcd', 6, too_short=too_short) + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + >>> def too_long(item_count): + ... print('The boss is going to hear about this') + >>> it = strictly_n('abcdef', 4, too_long=too_long) + >>> list(it) + The boss is going to hear about this + ['a', 'b', 'c', 'd'] + + """ + if too_short is None: + too_short = lambda item_count: raise_( + ValueError, + 'Too few items in iterable (got {})'.format(item_count), + ) + + if too_long is None: + too_long = lambda item_count: raise_( + ValueError, + 'Too many items in iterable (got at least {})'.format(item_count), + ) + + it = iter(iterable) + for i in range(n): + try: + item = next(it) + except StopIteration: + too_short(i) + return + else: + yield item try: next(it) except StopIteration: pass else: - raise too_long or ValueError('too many items in iterable (expected 1)') - - return value + too_long(n + 1) -def distinct_permutations(iterable): +def distinct_permutations(iterable, r=None): """Yield successive distinct permutations of the elements in *iterable*. >>> sorted(distinct_permutations([1, 0, 1])) @@ -553,34 +650,88 @@ def distinct_permutations(iterable): items input, and each `x_i` is the count of a distinct item in the input sequence. + If *r* is given, only the *r*-length permutations are yielded. + + >>> sorted(distinct_permutations([1, 0, 1], r=2)) + [(0, 1), (1, 0), (1, 1)] + >>> sorted(distinct_permutations(range(3), r=2)) + [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + """ - def perm_unique_helper(item_counts, perm, i): - """Internal helper function + # Algorithm: https://w.wiki/Qai + def _full(A): + while True: + # Yield the permutation we have + yield tuple(A) - :arg item_counts: Stores the unique items in ``iterable`` and how many - times they are repeated - :arg perm: The permutation that is being built for output - :arg i: The index of the permutation being modified + # Find the largest index i such that A[i] < A[i + 1] + for i in range(size - 2, -1, -1): + if A[i] < A[i + 1]: + break + # If no such index exists, this permutation is the last one + else: + return - The output permutations are built up recursively; the distinct items - are placed until their repetitions are exhausted. - """ - if i < 0: - yield tuple(perm) - else: - for item in item_counts: - if item_counts[item] <= 0: - continue - perm[i] = item - item_counts[item] -= 1 - for x in perm_unique_helper(item_counts, perm, i - 1): - yield x - item_counts[item] += 1 + # Find the largest index j greater than j such that A[i] < A[j] + for j in range(size - 1, i, -1): + if A[i] < A[j]: + break - item_counts = Counter(iterable) - length = sum(item_counts.values()) + # Swap the value of A[i] with that of A[j], then reverse the + # sequence from A[i + 1] to form the new permutation + A[i], A[j] = A[j], A[i] + A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] - return perm_unique_helper(item_counts, [None] * length, length - 1) + # Algorithm: modified from the above + def _partial(A, r): + # Split A into the first r items and the last r items + head, tail = A[:r], A[r:] + right_head_indexes = range(r - 1, -1, -1) + left_tail_indexes = range(len(tail)) + + while True: + # Yield the permutation we have + yield tuple(head) + + # Starting from the right, find the first index of the head with + # value smaller than the maximum value of the tail - call it i. + pivot = tail[-1] + for i in right_head_indexes: + if head[i] < pivot: + break + pivot = head[i] + else: + return + + # Starting from the left, find the first value of the tail + # with a value greater than head[i] and swap. + for j in left_tail_indexes: + if tail[j] > head[i]: + head[i], tail[j] = tail[j], head[i] + break + # If we didn't find one, start from the right and find the first + # index of the head with a value greater than head[i] and swap. + else: + for j in right_head_indexes: + if head[j] > head[i]: + head[i], head[j] = head[j], head[i] + break + + # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] + tail += head[: i - r : -1] # head[i + 1:][::-1] + i += 1 + head[i:], tail[:] = tail[: r - i], tail[r - i :] + + items = sorted(iterable) + + size = len(items) + if r is None: + r = size + + if 0 < r <= size: + return _full(items) if (r == size) else _partial(items, r) + + return iter(() if r else ((),)) def intersperse(e, iterable, n=1): @@ -597,8 +748,8 @@ def intersperse(e, iterable, n=1): if n == 0: raise ValueError('n must be > 0') elif n == 1: - # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... - # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... + # interleave(repeat(e), iterable) -> e, x_0, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, x_1, e, x_2... return islice(interleave(repeat(e), iterable), 1, None) else: # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... @@ -650,7 +801,7 @@ def windowed(seq, n, fillvalue=None, step=1): [(1, 2, 3), (2, 3, 4), (3, 4, 5)] When the window is larger than the iterable, *fillvalue* is used in place - of missing values:: + of missing values: >>> list(windowed([1, 2, 3], 4)) [(1, 2, 3, None)] @@ -660,6 +811,14 @@ def windowed(seq, n, fillvalue=None, step=1): >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] """ if n < 0: raise ValueError('n must be >= 0') @@ -669,37 +828,92 @@ def windowed(seq, n, fillvalue=None, step=1): if step < 1: raise ValueError('step must be >= 1') - it = iter(seq) - window = deque([], n) - append = window.append - - # Initial deque fill - for _ in range(n): - append(next(it, fillvalue)) - yield tuple(window) - - # Appending new items to the right causes old items to fall off the left - i = 0 - for item in it: - append(item) - i = (i + 1) % step - if i % step == 0: + window = deque(maxlen=n) + i = n + for _ in map(window.append, seq): + i -= 1 + if not i: + i = step yield tuple(window) - # If there are items from the iterable in the window, pad with the given - # value and emit them. - if (i % step) and (step - i < n): - for _ in range(step - i): - append(fillvalue) + size = len(window) + if size == 0: + return + elif size < n: + yield tuple(chain(window, repeat(fillvalue, n - size))) + elif 0 < i < min(step, n): + window += (fillvalue,) * i yield tuple(window) -class bucket(object): +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iter(iterable): + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: """Wrap *iterable* and return an object that buckets it iterable into child iterables based on a *key* function. >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] - >>> s = bucket(iterable, key=lambda x: x[0]) + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] >>> a_iterable = s['a'] >>> next(a_iterable) 'a1' @@ -727,6 +941,7 @@ class bucket(object): [] """ + def __init__(self, iterable, key, validator=None): self._it = iter(iterable) self._key = key @@ -772,6 +987,14 @@ class bucket(object): elif self._validator(item_value): self._cache[item_value].append(item) + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + yield from self._cache.keys() + def __getitem__(self, value): if not self._validator(value): return iter(()) @@ -819,7 +1042,7 @@ def spy(iterable, n=1): it = iter(iterable) head = take(n, it) - return head, chain(head, it) + return head.copy(), chain(head, it) def interleave(*iterables): @@ -852,6 +1075,72 @@ def interleave_longest(*iterables): return (x for x in i if x is not _marker) +def interleave_evenly(iterables, lengths=None): + """ + Interleave multiple iterables so that their elements are evenly distributed + throughout the output sequence. + + >>> iterables = [1, 2, 3, 4, 5], ['a', 'b'] + >>> list(interleave_evenly(iterables)) + [1, 2, 'a', 3, 4, 'b', 5] + + >>> iterables = [[1, 2, 3], [4, 5], [6, 7, 8]] + >>> list(interleave_evenly(iterables)) + [1, 6, 4, 2, 7, 3, 8, 5] + + This function requires iterables of known length. Iterables without + ``__len__()`` can be used by manually specifying lengths with *lengths*: + + >>> from itertools import combinations, repeat + >>> iterables = [combinations(range(4), 2), ['a', 'b', 'c']] + >>> lengths = [4 * (4 - 1) // 2, 3] + >>> list(interleave_evenly(iterables, lengths=lengths)) + [(0, 1), (0, 2), 'a', (0, 3), (1, 2), 'b', (1, 3), (2, 3), 'c'] + + Based on Bresenham's algorithm. + """ + if lengths is None: + try: + lengths = [len(it) for it in iterables] + except TypeError: + raise ValueError( + 'Iterable lengths could not be determined automatically. ' + 'Specify them with the lengths keyword.' + ) + elif len(iterables) != len(lengths): + raise ValueError('Mismatching number of iterables and lengths.') + + dims = len(lengths) + + # sort iterables by length, descending + lengths_permute = sorted( + range(dims), key=lambda i: lengths[i], reverse=True + ) + lengths_desc = [lengths[i] for i in lengths_permute] + iters_desc = [iter(iterables[i]) for i in lengths_permute] + + # the longest iterable is the primary one (Bresenham: the longest + # distance along an axis) + delta_primary, deltas_secondary = lengths_desc[0], lengths_desc[1:] + iter_primary, iters_secondary = iters_desc[0], iters_desc[1:] + errors = [delta_primary // dims] * len(deltas_secondary) + + to_yield = sum(lengths) + while to_yield: + yield next(iter_primary) + to_yield -= 1 + # update errors for each secondary iterable + errors = [e - delta for e, delta in zip(errors, deltas_secondary)] + + # those iterables for which the error is negative are yielded + # ("diagonal step" in Bresenham) + for i, e in enumerate(errors): + if e < 0: + yield next(iters_secondary[i]) + to_yield -= 1 + errors[i] += delta_primary + + def collapse(iterable, base_type=None, levels=None): """Flatten an iterable with multiple levels of nesting (e.g., a list of lists of tuples) into non-iterable types. @@ -860,7 +1149,9 @@ def collapse(iterable, base_type=None, levels=None): >>> list(collapse(iterable)) [1, 2, 3, 4, 5, 6] - String types are not considered iterable and will not be collapsed. + Binary and text strings are not considered iterable and + will not be collapsed. + To avoid collapsing other types, specify *base_type*: >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] @@ -876,11 +1167,12 @@ def collapse(iterable, base_type=None, levels=None): ['a', ['b'], 'c', ['d']] """ + def walk(node, level): if ( - ((levels is not None) and (level > levels)) or - isinstance(node, string_types) or - ((base_type is not None) and isinstance(node, base_type)) + ((levels is not None) and (level > levels)) + or isinstance(node, (str, bytes)) + or ((base_type is not None) and isinstance(node, base_type)) ): yield node return @@ -892,11 +1184,9 @@ def collapse(iterable, base_type=None, levels=None): return else: for child in tree: - for x in walk(child, level + 1): - yield x + yield from walk(child, level + 1) - for x in walk(iterable, 0): - yield x + yield from walk(iterable, 0) def side_effect(func, iterable, chunk_size=None, before=None, after=None): @@ -954,56 +1244,93 @@ def side_effect(func, iterable, chunk_size=None, before=None, after=None): else: for chunk in chunked(iterable, chunk_size): func(chunk) - for item in chunk: - yield item + yield from chunk finally: if after is not None: after() -def sliced(seq, n): +def sliced(seq, n, strict=False): """Yield slices of length *n* from the sequence *seq*. - >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) - [(1, 2, 3), (4, 5, 6)] + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] - If the length of the sequence is not divisible by the requested slice - length, the last slice will be shorter. + By the default, the last yielded slice will have fewer than *n* elements + if the length of *seq* is not divisible by *n*: - >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) - [(1, 2, 3), (4, 5, 6), (7, 8)] + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + If the length of *seq* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + slice is yielded. This function will only work for iterables that support slicing. For non-sliceable iterables, see :func:`chunked`. """ - return takewhile(bool, (seq[i: i + n] for i in count(0, n))) + iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) + if strict: + + def ret(): + for _slice in iterator: + if len(_slice) != n: + raise ValueError("seq is not divisible by n.") + yield _slice + + return iter(ret()) + else: + return iterator -def split_at(iterable, pred): +def split_at(iterable, pred, maxsplit=-1, keep_separator=False): """Yield lists of items from *iterable*, where each list is delimited by - an item where callable *pred* returns ``True``. The lists do not include - the delimiting items. + an item where callable *pred* returns ``True``. >>> list(split_at('abcdcba', lambda x: x == 'b')) [['a'], ['c', 'd', 'c'], ['a']] >>> list(split_at(range(10), lambda n: n % 2 == 1)) [[0], [2], [4], [6], [8], []] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) + [[0], [2], [4, 5, 6, 7, 8, 9]] + + By default, the delimiting items are not included in the output. + The include them, set *keep_separator* to ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) + [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] + """ + if maxsplit == 0: + yield list(iterable) + return + buf = [] - for item in iterable: + it = iter(iterable) + for item in it: if pred(item): yield buf + if keep_separator: + yield [item] + if maxsplit == 1: + yield list(it) + return buf = [] + maxsplit -= 1 else: buf.append(item) yield buf -def split_before(iterable, pred): - """Yield lists of items from *iterable*, where each list starts with an - item where callable *pred* returns ``True``: +def split_before(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: >>> list(split_before('OneTwo', lambda s: s.isupper())) [['O', 'n', 'e'], ['T', 'w', 'o']] @@ -1011,17 +1338,32 @@ def split_before(iterable, pred): >>> list(split_before(range(10), lambda n: n % 3 == 0)) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] """ + if maxsplit == 0: + yield list(iterable) + return + buf = [] - for item in iterable: + it = iter(iterable) + for item in it: if pred(item) and buf: yield buf + if maxsplit == 1: + yield [item] + list(it) + return buf = [] + maxsplit -= 1 buf.append(item) - yield buf + if buf: + yield buf -def split_after(iterable, pred): +def split_after(iterable, pred, maxsplit=-1): """Yield lists of items from *iterable*, where each list ends with an item where callable *pred* returns ``True``: @@ -1031,17 +1373,122 @@ def split_after(iterable, pred): >>> list(split_after(range(10), lambda n: n % 3 == 0)) [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] + """ + if maxsplit == 0: + yield list(iterable) + return + buf = [] - for item in iterable: + it = iter(iterable) + for item in it: buf.append(item) if pred(item) and buf: yield buf + if maxsplit == 1: + yield list(it) + return buf = [] + maxsplit -= 1 if buf: yield buf +def split_when(iterable, pred, maxsplit=-1): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], + ... lambda x, y: x > y, maxsplit=2)) + [[1, 2, 3, 3], [2, 5], [2, 4, 2]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + if maxsplit == 1: + yield [next_item] + list(it) + return + buf = [] + maxsplit -= 1 + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that itertools.slice + does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + def padded(iterable, fillvalue=None, n=None, next_multiple=False): """Yield the elements from *iterable*, followed by *fillvalue*, such that at least *n* items are emitted. @@ -1060,8 +1507,7 @@ def padded(iterable, fillvalue=None, n=None, next_multiple=False): """ it = iter(iterable) if n is None: - for item in chain(it, repeat(fillvalue)): - yield item + yield from chain(it, repeat(fillvalue)) elif n < 1: raise ValueError('n must be at least 1') else: @@ -1075,6 +1521,34 @@ def padded(iterable, fillvalue=None, n=None, next_multiple=False): yield fillvalue +def repeat_each(iterable, n=2): + """Repeat each element in *iterable* *n* times. + + >>> list(repeat_each('ABC', 3)) + ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'] + """ + return chain.from_iterable(map(repeat, iterable, repeat(n))) + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + def distribute(n, iterable): """Distribute the items from *iterable* among *n* smaller iterables. @@ -1138,7 +1612,38 @@ def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): ) -def zip_offset(*iterables, **kwargs): +def zip_equal(*iterables): + """``zip`` the input *iterables* together, but raise + ``UnequalIterablesError`` if they aren't all the same length. + + >>> it_1 = range(3) + >>> it_2 = iter('abc') + >>> list(zip_equal(it_1, it_2)) + [(0, 'a'), (1, 'b'), (2, 'c')] + + >>> it_1 = range(3) + >>> it_2 = iter('abcd') + >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + more_itertools.more.UnequalIterablesError: Iterables have different + lengths + + """ + if hexversion >= 0x30A00A6: + warnings.warn( + ( + 'zip_equal will be removed in a future version of ' + 'more-itertools. Use the builtin zip function with ' + 'strict=True instead.' + ), + DeprecationWarning, + ) + + return _zip_equal(*iterables) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): """``zip`` the input *iterables* together, but offset the `i`-th iterable by the `i`-th item in *offsets*. @@ -1146,7 +1651,7 @@ def zip_offset(*iterables, **kwargs): [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] This can be used as a lightweight alternative to SciPy or pandas to analyze - data sets in which somes series have a lead or lag relationship. + data sets in which some series have a lead or lag relationship. By default, the sequence will end when the shortest iterable is exhausted. To continue until the longest iterable is exhausted, set *longest* to @@ -1159,10 +1664,6 @@ def zip_offset(*iterables, **kwargs): sequence. Specify *fillvalue* to use some other value. """ - offsets = kwargs['offsets'] - longest = kwargs.get('longest', False) - fillvalue = kwargs.get('fillvalue', None) - if len(iterables) != len(offsets): raise ValueError("Number of iterables and offsets didn't match") @@ -1181,7 +1682,7 @@ def zip_offset(*iterables, **kwargs): return zip(*staggered) -def sort_together(iterables, key_list=(0,), reverse=False): +def sort_together(iterables, key_list=(0,), key=None, reverse=False): """Return the input iterables sorted together, with *key_list* as the priority for sorting. All iterables are trimmed to the length of the shortest one. @@ -1197,21 +1698,103 @@ def sort_together(iterables, key_list=(0,), reverse=False): [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] Set a different key list to sort according to another iterable. - Specifying mutliple keys dictates how ties are broken:: + Specifying multiple keys dictates how ties are broken:: >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] >>> sort_together(iterables, key_list=(1, 2)) [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + To sort by a function of the elements of the iterable, pass a *key* + function. Its arguments are the elements of the iterables corresponding to + the key list:: + + >>> names = ('a', 'b', 'c') + >>> lengths = (1, 2, 3) + >>> widths = (5, 2, 1) + >>> def area(length, width): + ... return length * width + >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) + [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] + Set *reverse* to ``True`` to sort in descending order. >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) [(3, 2, 1), ('a', 'b', 'c')] """ - return list(zip(*sorted(zip(*iterables), - key=itemgetter(*key_list), - reverse=reverse))) + if key is None: + # if there is no key function, the key argument to sorted is an + # itemgetter + key_argument = itemgetter(*key_list) + else: + # if there is a key function, call it with the items at the offsets + # specified by the key function as arguments + key_list = list(key_list) + if len(key_list) == 1: + # if key_list contains a single item, pass the item at that offset + # as the only argument to the key function + key_offset = key_list[0] + key_argument = lambda zipped_items: key(zipped_items[key_offset]) + else: + # if key_list contains multiple items, use itemgetter to return a + # tuple of items, which we pass as *args to the key function + get_key_items = itemgetter(*key_list) + key_argument = lambda zipped_items: key( + *get_key_items(zipped_items) + ) + + return list( + zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iter(iterable)) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + def itemgetter(i): + def getter(obj): + try: + return obj[i] + except IndexError: + # basically if we have an iterable like + # iter([(1, 2, 3), (4, 5), (6,)]) + # the second unzipped iterable would fail at the third tuple + # since it would try to access tup[1] + # same with the third unzipped iterable and the second tuple + # to support these "improperly zipped" iterables, + # we create a custom itemgetter + # which just stops the unzipped iterables + # at first length mismatch + raise StopIteration + + return getter + + return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) def divide(n, iterable): @@ -1246,19 +1829,26 @@ def divide(n, iterable): if n < 1: raise ValueError('n must be at least 1') - seq = tuple(iterable) + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + q, r = divmod(len(seq), n) ret = [] - for i in range(n): - start = (i * q) + (i if i < r else r) - stop = ((i + 1) * q) + (i + 1 if i + 1 < r else r) + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q ret.append(iter(seq[start:stop])) return ret -def always_iterable(obj, base_type=(text_type, binary_type)): +def always_iterable(obj, base_type=(str, bytes)): """If *obj* is iterable, return an iterator over its items:: >>> obj = (1, 2, 3) @@ -1350,21 +1940,23 @@ def adjacent(predicate, iterable, distance=1): return zip(adjacent_to_selected, i2) -def groupby_transform(iterable, keyfunc=None, valuefunc=None): - """An extension of :func:`itertools.groupby` that transforms the values of - *iterable* after grouping them. - *keyfunc* is a function used to compute a grouping key for each item. - *valuefunc* is a function for transforming the items after grouping. +def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): + """An extension of :func:`itertools.groupby` that can apply transformations + to the grouped data. - >>> iterable = 'AaaABbBCcA' - >>> keyfunc = lambda x: x.upper() - >>> valuefunc = lambda x: x.lower() - >>> grouper = groupby_transform(iterable, keyfunc, valuefunc) - >>> [(k, ''.join(g)) for k, g in grouper] - [('A', 'aaaa'), ('B', 'bbb'), ('C', 'cc'), ('A', 'a')] + * *keyfunc* is a function computing a key value for each item in *iterable* + * *valuefunc* is a function that transforms the individual items from + *iterable* after grouping + * *reducefunc* is a function that transforms each group of items - *keyfunc* and *valuefunc* default to identity functions if they are not - specified. + >>> iterable = 'aAAbBBcCC' + >>> keyfunc = lambda k: k.upper() + >>> valuefunc = lambda v: v.lower() + >>> reducefunc = lambda g: ''.join(g) + >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) + [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] + + Each optional argument defaults to an identity function if not specified. :func:`groupby_transform` is useful when grouping elements of an iterable using a separate iterable as the key. To do this, :func:`zip` the iterables @@ -1384,11 +1976,16 @@ def groupby_transform(iterable, keyfunc=None, valuefunc=None): duplicate groups, you should sort the iterable by the key function. """ - valuefunc = (lambda x: x) if valuefunc is None else valuefunc - return ((k, map(valuefunc, g)) for k, g in groupby(iterable, keyfunc)) + ret = groupby(iterable, keyfunc) + if valuefunc: + ret = ((k, map(valuefunc, g)) for k, g in ret) + if reducefunc: + ret = ((k, reducefunc(g)) for k, g in ret) + + return ret -def numeric_range(*args): +class numeric_range(abc.Sequence, abc.Hashable): """An extension of the built-in ``range()`` function whose arguments can be any orderable numeric type. @@ -1425,28 +2022,184 @@ def numeric_range(*args): Be aware of the limitations of floating point numbers; the representation of the yielded numbers may be surprising. - """ - argc = len(args) - if argc == 1: - stop, = args - start = type(stop)(0) - step = 1 - elif argc == 2: - start, stop = args - step = 1 - elif argc == 3: - start, stop, step = args - else: - err_msg = 'numeric_range takes at most 3 arguments, got {}' - raise TypeError(err_msg.format(argc)) + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: - values = (start + (step * n) for n in count()) - if step > 0: - return takewhile(partial(gt, stop), values) - elif step < 0: - return takewhile(partial(lt, stop), values) - else: - raise ValueError('numeric_range arg 3 must not be zero') + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = iter(numeric_range(start, stop, step)) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + + _EMPTY_HASH = hash(range(0, 0)) + + def __init__(self, *args): + argc = len(args) + if argc == 1: + (self._stop,) = args + self._start = type(self._stop)(0) + self._step = type(self._stop - self._start)(1) + elif argc == 2: + self._start, self._stop = args + self._step = type(self._stop - self._start)(1) + elif argc == 3: + self._start, self._stop, self._step = args + elif argc == 0: + raise TypeError( + 'numeric_range expected at least ' + '1 argument, got {}'.format(argc) + ) + else: + raise TypeError( + 'numeric_range expected at most ' + '3 arguments, got {}'.format(argc) + ) + + self._zero = type(self._step)(0) + if self._step == self._zero: + raise ValueError('numeric_range() arg 3 must not be zero') + self._growing = self._step > self._zero + self._init_len() + + def __bool__(self): + if self._growing: + return self._start < self._stop + else: + return self._start > self._stop + + def __contains__(self, elem): + if self._growing: + if self._start <= elem < self._stop: + return (elem - self._start) % self._step == self._zero + else: + if self._start >= elem > self._stop: + return (self._start - elem) % (-self._step) == self._zero + + return False + + def __eq__(self, other): + if isinstance(other, numeric_range): + empty_self = not bool(self) + empty_other = not bool(other) + if empty_self or empty_other: + return empty_self and empty_other # True if both empty + else: + return ( + self._start == other._start + and self._step == other._step + and self._get_by_index(-1) == other._get_by_index(-1) + ) + else: + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self._get_by_index(key) + elif isinstance(key, slice): + step = self._step if key.step is None else key.step * self._step + + if key.start is None or key.start <= -self._len: + start = self._start + elif key.start >= self._len: + start = self._stop + else: # -self._len < key.start < self._len + start = self._get_by_index(key.start) + + if key.stop is None or key.stop >= self._len: + stop = self._stop + elif key.stop <= -self._len: + stop = self._start + else: # -self._len < key.stop < self._len + stop = self._get_by_index(key.stop) + + return numeric_range(start, stop, step) + else: + raise TypeError( + 'numeric range indices must be ' + 'integers or slices, not {}'.format(type(key).__name__) + ) + + def __hash__(self): + if self: + return hash((self._start, self._get_by_index(-1), self._step)) + else: + return self._EMPTY_HASH + + def __iter__(self): + values = (self._start + (n * self._step) for n in count()) + if self._growing: + return takewhile(partial(gt, self._stop), values) + else: + return takewhile(partial(lt, self._stop), values) + + def __len__(self): + return self._len + + def _init_len(self): + if self._growing: + start = self._start + stop = self._stop + step = self._step + else: + start = self._stop + stop = self._start + step = -self._step + distance = stop - start + if distance <= self._zero: + self._len = 0 + else: # distance > 0 and step > 0: regular euclidean division + q, r = divmod(distance, step) + self._len = int(q) + int(r != self._zero) + + def __reduce__(self): + return numeric_range, (self._start, self._stop, self._step) + + def __repr__(self): + if self._step == 1: + return "numeric_range({}, {})".format( + repr(self._start), repr(self._stop) + ) + else: + return "numeric_range({}, {}, {})".format( + repr(self._start), repr(self._stop), repr(self._step) + ) + + def __reversed__(self): + return iter( + numeric_range( + self._get_by_index(-1), self._start - self._step, -self._step + ) + ) + + def count(self, value): + return int(value in self) + + def index(self, value): + if self._growing: + if self._start <= value < self._stop: + q, r = divmod(value - self._start, self._step) + if r == self._zero: + return int(q) + else: + if self._start >= value > self._stop: + q, r = divmod(self._start - value, -self._step) + if r == self._zero: + return int(q) + + raise ValueError("{} is not in numeric range".format(value)) + + def _get_by_index(self, i): + if i < 0: + i += self._len + if i < 0 or i >= self._len: + raise IndexError("numeric range object index out of range") + return self._start + i * self._step def count_cycle(iterable, n=None): @@ -1465,6 +2218,43 @@ def count_cycle(iterable, n=None): return ((i, item) for i in counter for item in iterable) +def mark_ends(iterable): + """Yield 3-tuples of the form ``(is_first, is_last, item)``. + + >>> list(mark_ends('ABC')) + [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] + + Use this when looping over an iterable to take special action on its first + and/or last items: + + >>> iterable = ['Header', 100, 200, 'Footer'] + >>> total = 0 + >>> for is_first, is_last, item in mark_ends(iterable): + ... if is_first: + ... continue # Skip the header + ... if is_last: + ... continue # Skip the footer + ... total += item + >>> print(total) + 300 + """ + it = iter(iterable) + + try: + b = next(it) + except StopIteration: + return + + try: + for i in count(): + a = b + b = next(it) + yield i == 0, False, a + + except StopIteration: + yield i == 0, True, a + + def locate(iterable, pred=bool, window_size=None): """Yield the index of each item in *iterable* for which *pred* returns ``True``. @@ -1513,6 +2303,16 @@ def locate(iterable, pred=bool, window_size=None): return compress(count(), starmap(pred, it)) +def longest_common_prefix(iterables): + """Yield elements of the longest common prefix amongst given *iterables*. + + >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf'])) + 'ab' + + """ + return (c[0] for c in takewhile(all_equal, zip(*iterables))) + + def lstrip(iterable, pred): """Yield the items from *iterable*, but strip any from the beginning for which *pred* returns ``True``. @@ -1547,13 +2347,13 @@ def rstrip(iterable, pred): """ cache = [] cache_append = cache.append + cache_clear = cache.clear for x in iterable: if pred(x): cache_append(x) else: - for y in cache: - yield y - del cache[:] + yield from cache + cache_clear() yield x @@ -1574,7 +2374,7 @@ def strip(iterable, pred): return rstrip(lstrip(iterable, pred), pred) -def islice_extended(iterable, *args): +class islice_extended: """An extension of :func:`itertools.islice` that supports negative values for *stop*, *start*, and *step*. @@ -1591,20 +2391,46 @@ def islice_extended(iterable, *args): >>> list(islice_extended(count(), 110, 99, -2)) [110, 108, 106, 104, 102, 100] + You can also use slice notation directly: + + >>> iterable = map(str, count()) + >>> it = islice_extended(iterable)[10:20:2] + >>> list(it) + ['10', '12', '14', '16', '18'] + """ - s = slice(*args) + + def __init__(self, iterable, *args): + it = iter(iterable) + if args: + self._iterable = _islice_helper(it, slice(*args)) + else: + self._iterable = it + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterable) + + def __getitem__(self, key): + if isinstance(key, slice): + return islice_extended(_islice_helper(self._iterable, key)) + + raise TypeError('islice_extended.__getitem__ argument must be a slice') + + +def _islice_helper(it, s): start = s.start stop = s.stop if s.step == 0: raise ValueError('step argument must be a non-zero integer or None.') step = s.step or 1 - it = iter(iterable) - if step > 0: start = 0 if (start is None) else start - if (start < 0): + if start < 0: # Consume all but the last -start items cache = deque(enumerate(it, 1), maxlen=-start) len_iter = cache[-1][0] if cache else 0 @@ -1642,8 +2468,7 @@ def islice_extended(iterable, *args): cache.append(item) else: # When both start and stop are positive we have the normal case - for item in islice(it, start, stop, step): - yield item + yield from islice(it, start, stop, step) else: start = -1 if (start is None) else start @@ -1688,8 +2513,7 @@ def islice_extended(iterable, *args): cache = list(islice(it, n)) - for item in cache[i::step]: - yield item + yield from cache[i::step] def always_reversible(iterable): @@ -1740,6 +2564,17 @@ def consecutive_groups(iterable, ordering=lambda x: x): ['i'] ['l', 'm', 'n', 'o', 'p'] + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + """ for k, g in groupby( enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) @@ -1747,48 +2582,52 @@ def consecutive_groups(iterable, ordering=lambda x: x): yield map(itemgetter(1), g) -def difference(iterable, func=sub): - """By default, compute the first difference of *iterable* using - :func:`operator.sub`. +def difference(iterable, func=sub, *, initial=None): + """This function is the inverse of :func:`itertools.accumulate`. By default + it will compute the first difference of *iterable* using + :func:`operator.sub`: - >>> iterable = [0, 1, 3, 6, 10] + >>> from itertools import accumulate + >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 >>> list(difference(iterable)) [0, 1, 2, 3, 4] - This is the opposite of :func:`accumulate`'s default behavior: - - >>> from more_itertools import accumulate - >>> iterable = [0, 1, 2, 3, 4] - >>> list(accumulate(iterable)) - [0, 1, 3, 6, 10] - >>> list(difference(accumulate(iterable))) - [0, 1, 2, 3, 4] - - By default *func* is :func:`operator.sub`, but other functions can be + *func* defaults to :func:`operator.sub`, but other functions can be specified. They will be applied as follows:: A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... For example, to do progressive division: - >>> iterable = [1, 2, 6, 24, 120] # Factorial sequence + >>> iterable = [1, 2, 6, 24, 120] >>> func = lambda x, y: x // y >>> list(difference(iterable, func)) [1, 2, 3, 4, 5] + If the *initial* keyword is set, the first element will be skipped when + computing successive differences. + + >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) + >>> list(difference(it, initial=10)) + [1, 2, 3] + """ a, b = tee(iterable) try: - item = next(b) + first = [next(b)] except StopIteration: return iter([]) - return chain([item], map(lambda x: func(x[1], x[0]), zip(a, b))) + + if initial is not None: + first = [] + + return chain(first, map(func, b, a)) class SequenceView(Sequence): """Return a read-only view of the sequence object *target*. - :class:`SequenceView` objects are analagous to Python's built-in + :class:`SequenceView` objects are analogous to Python's built-in "dictionary view" types. They provide a dynamic view of a sequence's items, meaning that when the sequence updates, so does the view. @@ -1814,6 +2653,7 @@ class SequenceView(Sequence): require (much) extra storage. """ + def __init__(self, target): if not isinstance(target, Sequence): raise TypeError @@ -1829,7 +2669,7 @@ class SequenceView(Sequence): return '{}({})'.format(self.__class__.__name__, repr(self._target)) -class seekable(object): +class seekable: """Wrap an iterator to allow for seeking backward and forward. This progressively caches the items in the source iterable so they can be re-visited. @@ -1862,8 +2702,26 @@ class seekable(object): >>> next(it), next(it), next(it) ('0', '1', '2') - The cache grows as the source iterable progresses, so beware of wrapping - very large or infinite iterables. + Call :meth:`peek` to look ahead one item without advancing the iterator: + + >>> it = seekable('1234') + >>> it.peek() + '1' + >>> list(it) + ['1', '2', '3', '4'] + >>> it.peek(default='empty') + 'empty' + + Before the iterator is at its end, calling :func:`bool` on it will return + ``True``. After it will return ``False``: + + >>> it = seekable('5678') + >>> bool(it) + True + >>> list(it) + ['5', '6', '7', '8'] + >>> bool(it) + False You may view the contents of the cache with the :meth:`elements` method. That returns a :class:`SequenceView`, a view that updates automatically: @@ -1879,11 +2737,30 @@ class seekable(object): >>> elements SequenceView(['0', '1', '2', '3']) + By default, the cache grows as the source iterable progresses, so beware of + wrapping very large or infinite iterables. Supply *maxlen* to limit the + size of the cache (this of course limits how far back you can seek). + + >>> from itertools import count + >>> it = seekable((str(n) for n in count()), maxlen=2) + >>> next(it), next(it), next(it), next(it) + ('0', '1', '2', '3') + >>> list(it.elements()) + ['2', '3'] + >>> it.seek(0) + >>> next(it), next(it), next(it), next(it) + ('2', '3', '4', '5') + >>> next(it) + '6' + """ - def __init__(self, iterable): + def __init__(self, iterable, maxlen=None): self._source = iter(iterable) - self._cache = [] + if maxlen is None: + self._cache = [] + else: + self._cache = deque([], maxlen) self._index = None def __iter__(self): @@ -1903,7 +2780,24 @@ class seekable(object): self._cache.append(item) return item - next = __next__ + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + try: + peeked = next(self) + except StopIteration: + if default is _marker: + raise + return default + if self._index is None: + self._index = len(self._cache) + self._index -= 1 + return peeked def elements(self): return SequenceView(self._cache) @@ -1915,7 +2809,7 @@ class seekable(object): consume(self, remainder) -class run_length(object): +class run_length: """ :func:`run_length.encode` compresses an iterable with run-length encoding. It yields groups of repeated items with the count of how many times they @@ -1965,8 +2859,8 @@ def exactly_n(iterable, n, predicate=bool): def circular_shifts(iterable): """Return a list of circular shifts of *iterable*. - >>> circular_shifts(range(4)) - [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + >>> circular_shifts(range(4)) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] """ lst = list(iterable) return take(len(lst), windowed(cycle(lst), len(lst))) @@ -2140,9 +3034,7 @@ def rlocate(iterable, pred=bool, window_size=None): if window_size is None: try: len_iter = len(iterable) - return ( - len_iter - i - 1 for i in locate(reversed(iterable), pred) - ) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) except TypeError: pass @@ -2200,8 +3092,7 @@ def replace(iterable, pred, substitutes, count=None, window_size=1): if pred(*w): if (count is None) or (n < count): n += 1 - for s in substitutes: - yield s + yield from substitutes consume(windows, window_size - 1) continue @@ -2209,3 +3100,1248 @@ def replace(iterable, pred, substitutes, count=None, window_size=1): # yield the first item from the window. if w and (w[0] is not _marker): yield w[0] + + +def partitions(iterable): + """Yield all possible order-preserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from set_partitions_helper(L, k) + else: + yield from set_partitions_helper(L, k) + + +class time_limited: + """ + Yield items from *iterable* until *limit_seconds* have passed. + If the time limit expires before all items have been yielded, the + ``timed_out`` parameter will be set to ``True``. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = time_limited(0.1, generator()) + >>> list(iterable) + [1, 2] + >>> iterable.timed_out + True + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + + """ + + def __init__(self, limit_seconds, iterable): + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + self.limit_seconds = limit_seconds + self._iterable = iter(iterable) + self._start_time = monotonic() + self.timed_out = False + + def __iter__(self): + return self + + def __next__(self): + item = next(self._iterable) + if monotonic() - self._start_time > self.limit_seconds: + self.timed_out = True + raise StopIteration + + return item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +class _IChunk: + def __init__(self, iterable, n): + self._it = islice(iterable, n) + self._cache = deque() + + def fill_cache(self): + self._cache.extend(self._it) + + def __iter__(self): + return self + + def __next__(self): + try: + return next(self._it) + except StopIteration: + if self._cache: + return self._cache.popleft() + else: + raise + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + source = peekable(iter(iterable)) + ichunk_marker = object() + while True: + # Check to see whether we're at the end of the source iterable + item = source.peek(ichunk_marker) + if item is ichunk_marker: + return + + chunk = _IChunk(source, n) + yield chunk + + # Advance the source iterable and fill previous chunk's cache + chunk.fill_cache() + + +def iequals(*iterables): + """Return ``True`` if all given *iterables* are equal to each other, + which means that they contain the same elements in the same order. + + The function is useful for comparing iterables of different data types + or iterables that do not support equality checks. + + >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc")) + True + + >>> iequals("abc", "acb") + False + + Not to be confused with :func:`all_equals`, which checks whether all + elements of iterable are equal to each other. + + """ + return all(map(all_equal, zip_longest(*iterables, fillvalue=object()))) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + return + pool = tuple(iterable) + generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] + current_combo = [None] * r + level = 0 + while generators: + try: + cur_idx, p = next(generators[-1]) + except StopIteration: + generators.pop() + level -= 1 + continue + current_combo[level] = p + if level + 1 == r: + yield tuple(current_combo) + else: + generators.append( + unique_everseen( + enumerate(pool[cur_idx + 1 :], cur_idx + 1), + key=itemgetter(1), + ) + ) + level += 1 + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def map_if(iterable, pred, func, func_else=lambda x: x): + """Evaluate each item from *iterable* using *pred*. If the result is + equivalent to ``True``, transform the item with *func* and yield it. + Otherwise, transform the item with *func_else* and yield it. + + *pred*, *func*, and *func_else* should each be functions that accept + one argument. By default, *func_else* is the identity function. + + >>> from math import sqrt + >>> iterable = list(range(-5, 5)) + >>> iterable + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] + >>> list(map_if(iterable, lambda x: x > 3, lambda x: 'toobig')) + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig'] + >>> list(map_if(iterable, lambda x: x >= 0, + ... lambda x: f'{sqrt(x):.2f}', lambda x: None)) + [None, None, None, None, None, '0.00', '1.00', '1.41', '1.73', '2.00'] + """ + for item in iterable: + yield func(item) if pred(item) else func_else(item) + + +def _sample_unweighted(iterable, k): + # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + # Fill up the reservoir (collection of samples) with the first `k` samples + reservoir = take(k, iterable) + + # Generate random number that's the largest in a sample of k U(0,1) numbers + # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic + W = exp(log(random()) / k) + + # The number of elements to skip before changing the reservoir is a random + # number with a geometric distribution. Sample it using random() and logs. + next_index = k + floor(log(random()) / log(1 - W)) + + for index, element in enumerate(iterable, k): + + if index == next_index: + reservoir[randrange(k)] = element + # The new W is the largest in a sample of k U(0, `old_W`) numbers + W *= exp(log(random()) / k) + next_index += floor(log(random()) / log(1 - W)) + 1 + + return reservoir + + +def _sample_weighted(iterable, k, weights): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterable)) + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterable): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + # Equivalent to [element for weight_key, element in sorted(reservoir)] + return [heappop(reservoir)[1] for _ in range(k)] + + +def sample(iterable, k, weights=None): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. Like :func:`random.sample`, but works on iterables + of unknown length. + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + An iterable with *weights* may also be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + The algorithm can also be used to generate weighted random permutations. + The relative weight of each item determines the probability that it + appears late in the permutation. + + >>> data = "abcdefgh" + >>> weights = range(1, len(data) + 1) + >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP + ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + """ + if k == 0: + return [] + + iterable = iter(iterable) + if weights is None: + return _sample_unweighted(iterable, k) + else: + weights = iter(weights) + return _sample_weighted(iterable, k, weights) + + +def is_sorted(iterable, key=None, reverse=False, strict=False): + """Returns ``True`` if the items of iterable are in sorted order, and + ``False`` otherwise. *key* and *reverse* have the same meaning that they do + in the built-in :func:`sorted` function. + + >>> is_sorted(['1', '2', '3', '4', '5'], key=int) + True + >>> is_sorted([5, 4, 3, 1, 2], reverse=True) + False + + If *strict*, tests for strict sorting, that is, returns ``False`` if equal + elements are found: + + >>> is_sorted([1, 2, 2]) + True + >>> is_sorted([1, 2, 2], strict=True) + False + + The function returns ``False`` after encountering the first out-of-order + item. If there are no out-of-order items, the iterable is exhausted. + """ + + compare = (le if reverse else ge) if strict else (lt if reverse else gt) + it = iterable if key is None else map(key, iterable) + return not any(starmap(compare, pairwise(it))) + + +class AbortThread(BaseException): + pass + + +class callback_iter: + """Convert a function that uses callbacks to an iterator. + + Let *func* be a function that takes a `callback` keyword argument. + For example: + + >>> def func(callback=None): + ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: + ... if callback: + ... callback(i, c) + ... return 4 + + + Use ``with callback_iter(func)`` to get an iterator over the parameters + that are delivered to the callback. + + >>> with callback_iter(func) as it: + ... for args, kwargs in it: + ... print(args) + (1, 'a') + (2, 'b') + (3, 'c') + + The function will be called in a background thread. The ``done`` property + indicates whether it has completed execution. + + >>> it.done + True + + If it completes successfully, its return value will be available + in the ``result`` property. + + >>> it.result + 4 + + Notes: + + * If the function uses some keyword argument besides ``callback``, supply + *callback_kwd*. + * If it finished executing, but raised an exception, accessing the + ``result`` property will raise the same exception. + * If it hasn't finished executing, accessing the ``result`` + property from within the ``with`` block will raise ``RuntimeError``. + * If it hasn't finished executing, accessing the ``result`` property from + outside the ``with`` block will raise a + ``more_itertools.AbortThread`` exception. + * Provide *wait_seconds* to adjust how frequently the it is polled for + output. + + """ + + def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): + self._func = func + self._callback_kwd = callback_kwd + self._aborted = False + self._future = None + self._wait_seconds = wait_seconds + # Lazily import concurrent.future + self._executor = __import__( + 'concurrent.futures' + ).futures.ThreadPoolExecutor(max_workers=1) + self._iterator = self._reader() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._aborted = True + self._executor.shutdown() + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + @property + def done(self): + if self._future is None: + return False + return self._future.done() + + @property + def result(self): + if not self.done: + raise RuntimeError('Function has not yet completed') + + return self._future.result() + + def _reader(self): + q = Queue() + + def callback(*args, **kwargs): + if self._aborted: + raise AbortThread('canceled by user') + + q.put((args, kwargs)) + + self._future = self._executor.submit( + self._func, **{self._callback_kwd: callback} + ) + + while True: + try: + item = q.get(timeout=self._wait_seconds) + except Empty: + pass + else: + q.task_done() + yield item + + if self._future.done(): + break + + remaining = [] + while True: + try: + item = q.get_nowait() + except Empty: + break + else: + q.task_done() + remaining.append(item) + q.join() + yield from remaining + + +def windowed_complete(iterable, n): + """ + Yield ``(beginning, middle, end)`` tuples, where: + + * Each ``middle`` has *n* items from *iterable* + * Each ``beginning`` has the items before the ones in ``middle`` + * Each ``end`` has the items after the ones in ``middle`` + + >>> iterable = range(7) + >>> n = 3 + >>> for beginning, middle, end in windowed_complete(iterable, n): + ... print(beginning, middle, end) + () (0, 1, 2) (3, 4, 5, 6) + (0,) (1, 2, 3) (4, 5, 6) + (0, 1) (2, 3, 4) (5, 6) + (0, 1, 2) (3, 4, 5) (6,) + (0, 1, 2, 3) (4, 5, 6) () + + Note that *n* must be at least 0 and most equal to the length of + *iterable*. + + This function will exhaust the iterable and may require significant + storage. + """ + if n < 0: + raise ValueError('n must be >= 0') + + seq = tuple(iterable) + size = len(seq) + + if n > size: + raise ValueError('n must be <= len(seq)') + + for i in range(size - n + 1): + beginning = seq[:i] + middle = seq[i : i + n] + end = seq[i + n :] + yield beginning, middle, end + + +def all_unique(iterable, key=None): + """ + Returns ``True`` if all the elements of *iterable* are unique (no two + elements are equal). + + >>> all_unique('ABCB') + False + + If a *key* function is specified, it will be used to make comparisons. + + >>> all_unique('ABCb') + True + >>> all_unique('ABCb', str.lower) + False + + The function returns as soon as the first non-unique element is + encountered. Iterables with a mix of hashable and unhashable items can + be used, but the function will be slower for unhashable items. + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + for element in map(key, iterable) if key else iterable: + try: + if element in seenset: + return False + seenset_add(element) + except TypeError: + if element in seenlist: + return False + seenlist_add(element) + return True + + +def nth_product(index, *args): + """Equivalent to ``list(product(*args))[index]``. + + The products of *args* can be ordered lexicographically. + :func:`nth_product` computes the product at sort position *index* without + computing the previous products. + + >>> nth_product(8, range(2), range(2), range(2), range(2)) + (1, 0, 0, 0) + + ``IndexError`` will be raised if the given *index* is invalid. + """ + pools = list(map(tuple, reversed(args))) + ns = list(map(len, pools)) + + c = reduce(mul, ns) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + result = [] + for pool, n in zip(pools, ns): + result.append(pool[index % n]) + index //= n + + return tuple(reversed(result)) + + +def nth_permutation(iterable, r, index): + """Equivalent to ``list(permutations(iterable, r))[index]``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`nth_permutation` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences. + + >>> nth_permutation('ghijk', 2, 5) + ('h', 'i') + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = list(iterable) + n = len(pool) + + if r is None or r == n: + r, c = n, factorial(n) + elif not 0 <= r < n: + raise ValueError + else: + c = factorial(n) // factorial(n - r) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + if c == 0: + return tuple() + + result = [0] * r + q = index * factorial(n) // c if r < n else index + for d in range(1, n + 1): + q, i = divmod(q, d) + if 0 <= n - d < r: + result[n - d] = i + if q == 0: + break + + return tuple(map(pool.pop, result)) + + +def value_chain(*args): + """Yield all arguments passed to the function in the same order in which + they were passed. If an argument itself is iterable then iterate over its + values. + + >>> list(value_chain(1, 2, 3, [4, 5, 6])) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and are emitted + as-is: + + >>> list(value_chain('12', '34', ['56', '78'])) + ['12', '34', '56', '78'] + + + Multiple levels of nesting are not flattened. + + """ + for value in args: + if isinstance(value, (str, bytes)): + yield value + continue + try: + yield from value + except TypeError: + yield value + + +def product_index(element, *args): + """Equivalent to ``list(product(*args)).index(element)`` + + The products of *args* can be ordered lexicographically. + :func:`product_index` computes the first index of *element* without + computing the previous products. + + >>> product_index([8, 2], range(10), range(5)) + 42 + + ``ValueError`` will be raised if the given *element* isn't in the product + of *args*. + """ + index = 0 + + for x, pool in zip_longest(element, args, fillvalue=_marker): + if x is _marker or pool is _marker: + raise ValueError('element is not a product of args') + + pool = tuple(pool) + index = index * len(pool) + pool.index(x) + + return index + + +def combination_index(element, iterable): + """Equivalent to ``list(combinations(iterable, r)).index(element)`` + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`combination_index` computes the index of the + first *element*, without computing the previous combinations. + + >>> combination_index('adf', 'abcdefg') + 10 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations of *iterable*. + """ + element = enumerate(element) + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = enumerate(iterable) + for n, x in pool: + if x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + else: + raise ValueError('element is not a combination of iterable') + + n, _ = last(pool, default=(n, None)) + + # Python versions below 3.8 don't have math.comb + index = 1 + for i, j in enumerate(reversed(indexes), start=1): + j = n - j + if i <= j: + index += factorial(j) // (factorial(i) * factorial(j - i)) + + return factorial(n + 1) // (factorial(k + 1) * factorial(n - k)) - index + + +def permutation_index(element, iterable): + """Equivalent to ``list(permutations(iterable, r)).index(element)``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`permutation_index` + computes the index of the first *element* directly, without computing + the previous permutations. + + >>> permutation_index([1, 3, 2], range(5)) + 19 + + ``ValueError`` will be raised if the given *element* isn't one of the + permutations of *iterable*. + """ + index = 0 + pool = list(iterable) + for i, x in zip(range(len(pool), -1, -1), element): + r = pool.index(x) + index = index * i + r + del pool[r] + + return index + + +class countable: + """Wrap *iterable* and keep a count of how many items have been consumed. + + The ``items_seen`` attribute starts at ``0`` and increments as the iterable + is consumed: + + >>> iterable = map(str, range(10)) + >>> it = countable(iterable) + >>> it.items_seen + 0 + >>> next(it), next(it) + ('0', '1') + >>> list(it) + ['2', '3', '4', '5', '6', '7', '8', '9'] + >>> it.items_seen + 10 + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self.items_seen = 0 + + def __iter__(self): + return self + + def __next__(self): + item = next(self._it) + self.items_seen += 1 + + return item + + +def chunked_even(iterable, n): + """Break *iterable* into lists of approximately length *n*. + Items are distributed such the lengths of the lists differ by at most + 1 item. + + >>> iterable = [1, 2, 3, 4, 5, 6, 7] + >>> n = 3 + >>> list(chunked_even(iterable, n)) # List lengths: 3, 2, 2 + [[1, 2, 3], [4, 5], [6, 7]] + >>> list(chunked(iterable, n)) # List lengths: 3, 3, 1 + [[1, 2, 3], [4, 5, 6], [7]] + + """ + + len_method = getattr(iterable, '__len__', None) + + if len_method is None: + return _chunked_even_online(iterable, n) + else: + return _chunked_even_finite(iterable, len_method(), n) + + +def _chunked_even_online(iterable, n): + buffer = [] + maxbuf = n + (n - 2) * (n - 1) + for x in iterable: + buffer.append(x) + if len(buffer) == maxbuf: + yield buffer[:n] + buffer = buffer[n:] + yield from _chunked_even_finite(buffer, len(buffer), n) + + +def _chunked_even_finite(iterable, N, n): + if N < 1: + return + + # Lists are either size `full_size <= n` or `partial_size = full_size - 1` + q, r = divmod(N, n) + num_lists = q + (1 if r > 0 else 0) + q, r = divmod(N, num_lists) + full_size = q + (1 if r > 0 else 0) + partial_size = full_size - 1 + num_full = N - partial_size * num_lists + num_partial = num_lists - num_full + + buffer = [] + iterator = iter(iterable) + + # Yield num_full lists of full_size + for x in iterator: + buffer.append(x) + if len(buffer) == full_size: + yield buffer + buffer = [] + num_full -= 1 + if num_full <= 0: + break + + # Yield num_partial lists of partial_size + for x in iterator: + buffer.append(x) + if len(buffer) == partial_size: + yield buffer + buffer = [] + num_partial -= 1 + + +def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False): + """A version of :func:`zip` that "broadcasts" any scalar + (i.e., non-iterable) items into output tuples. + + >>> iterable_1 = [1, 2, 3] + >>> iterable_2 = ['a', 'b', 'c'] + >>> scalar = '_' + >>> list(zip_broadcast(iterable_1, iterable_2, scalar)) + [(1, 'a', '_'), (2, 'b', '_'), (3, 'c', '_')] + + The *scalar_types* keyword argument determines what types are considered + scalar. It is set to ``(str, bytes)`` by default. Set it to ``None`` to + treat strings and byte strings as iterable: + + >>> list(zip_broadcast('abc', 0, 'xyz', scalar_types=None)) + [('a', 0, 'x'), ('b', 0, 'y'), ('c', 0, 'z')] + + If the *strict* keyword argument is ``True``, then + ``UnequalIterablesError`` will be raised if any of the iterables have + different lengths. + """ + + def is_scalar(obj): + if scalar_types and isinstance(obj, scalar_types): + return True + try: + iter(obj) + except TypeError: + return True + else: + return False + + size = len(objects) + if not size: + return + + iterables, iterable_positions = [], [] + scalars, scalar_positions = [], [] + for i, obj in enumerate(objects): + if is_scalar(obj): + scalars.append(obj) + scalar_positions.append(i) + else: + iterables.append(iter(obj)) + iterable_positions.append(i) + + if len(scalars) == size: + yield tuple(objects) + return + + zipper = _zip_equal if strict else zip + for item in zipper(*iterables): + new_item = [None] * size + + for i, elem in zip(iterable_positions, item): + new_item[i] = elem + + for i, elem in zip(scalar_positions, scalars): + new_item[i] = elem + + yield tuple(new_item) + + +def unique_in_window(iterable, n, key=None): + """Yield the items from *iterable* that haven't been seen recently. + *n* is the size of the lookback window. + + >>> iterable = [0, 1, 0, 2, 3, 0] + >>> n = 3 + >>> list(unique_in_window(iterable, n)) + [0, 1, 2, 3, 0] + + The *key* function, if provided, will be used to determine uniqueness: + + >>> list(unique_in_window('abAcda', 3, key=lambda x: x.lower())) + ['a', 'b', 'c', 'd', 'a'] + + The items in *iterable* must be hashable. + + """ + if n <= 0: + raise ValueError('n must be greater than 0') + + window = deque(maxlen=n) + uniques = set() + use_key = key is not None + + for item in iterable: + k = key(item) if use_key else item + if k in uniques: + continue + + if len(uniques) == n: + uniques.discard(window[0]) + + uniques.add(k) + window.append(k) + + yield item + + +def duplicates_everseen(iterable, key=None): + """Yield duplicate elements after their first appearance. + + >>> list(duplicates_everseen('mississippi')) + ['s', 'i', 's', 's', 'i', 'p', 'i'] + >>> list(duplicates_everseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'A', 'a', 'a'] + + This function is analagous to :func:`unique_everseen` and is subject to + the same performance considerations. + + """ + seen_set = set() + seen_list = [] + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seen_set: + seen_set.add(k) + else: + yield element + except TypeError: + if k not in seen_list: + seen_list.append(k) + else: + yield element + + +def duplicates_justseen(iterable, key=None): + """Yields serially-duplicate elements after their first appearance. + + >>> list(duplicates_justseen('mississippi')) + ['s', 's', 'p'] + >>> list(duplicates_justseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'a', 'a'] + + This function is analagous to :func:`unique_justseen`. + + """ + return flatten( + map( + lambda group_tuple: islice_extended(group_tuple[1])[1:], + groupby(iterable, key), + ) + ) + + +def minmax(iterable_or_value, *others, key=None, default=_marker): + """Returns both the smallest and largest items in an iterable + or the largest of two or more arguments. + + >>> minmax([3, 1, 5]) + (1, 5) + + >>> minmax(4, 2, 6) + (2, 6) + + If a *key* function is provided, it will be used to transform the input + items for comparison. + + >>> minmax([5, 30], key=str) # '30' sorts before '5' + (30, 5) + + If a *default* value is provided, it will be returned if there are no + input items. + + >>> minmax([], default=(0, 0)) + (0, 0) + + Otherwise ``ValueError`` is raised. + + This function is based on the + `recipe `__ by + Raymond Hettinger and takes care to minimize the number of comparisons + performed. + """ + iterable = (iterable_or_value, *others) if others else iterable_or_value + + it = iter(iterable) + + try: + lo = hi = next(it) + except StopIteration as e: + if default is _marker: + raise ValueError( + '`minmax()` argument is an empty iterable. ' + 'Provide a `default` value to suppress this error.' + ) from e + return default + + # Different branches depending on the presence of key. This saves a lot + # of unimportant copies which would slow the "key=None" branch + # significantly down. + if key is None: + for x, y in zip_longest(it, it, fillvalue=lo): + if y < x: + x, y = y, x + if x < lo: + lo = x + if hi < y: + hi = y + + else: + lo_key = hi_key = key(lo) + + for x, y in zip_longest(it, it, fillvalue=lo): + + x_key, y_key = key(x), key(y) + + if y_key < x_key: + x, y, x_key, y_key = y, x, y_key, x_key + if x_key < lo_key: + lo, lo_key = x, x_key + if hi_key < y_key: + hi, hi_key = y, y_key + + return lo, hi + + +def constrained_batches( + iterable, max_size, max_count=None, get_len=len, strict=True +): + """Yield batches of items from *iterable* with a combined size limited by + *max_size*. + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10)) + [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')] + + If a *max_count* is supplied, the number of items per batch is also + limited: + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10, max_count = 2)) + [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)] + + If a *get_len* function is supplied, use that instead of :func:`len` to + determine item size. + + If *strict* is ``True``, raise ``ValueError`` if any single item is bigger + than *max_size*. Otherwise, allow single items to exceed *max_size*. + """ + if max_size <= 0: + raise ValueError('maximum size must be greater than zero') + + batch = [] + batch_size = 0 + batch_count = 0 + for item in iterable: + item_len = get_len(item) + if strict and item_len > max_size: + raise ValueError('item size exceeds maximum size') + + reached_count = batch_count == max_count + reached_size = item_len + batch_size > max_size + if batch_count and (reached_size or reached_count): + yield tuple(batch) + batch.clear() + batch_size = 0 + batch_count = 0 + + batch.append(item) + batch_size += item_len + batch_count += 1 + + if batch: + yield tuple(batch) diff --git a/libs/win/more_itertools/more.pyi b/libs/win/more_itertools/more.pyi new file mode 100644 index 00000000..1413fae7 --- /dev/null +++ b/libs/win/more_itertools/more.pyi @@ -0,0 +1,674 @@ +"""Stubs for more_itertools.more""" + +from typing import ( + Any, + Callable, + Container, + Dict, + Generic, + Hashable, + Iterable, + Iterator, + List, + Optional, + Reversible, + Sequence, + Sized, + Tuple, + Union, + TypeVar, + type_check_only, +) +from types import TracebackType +from typing_extensions import ContextManager, Protocol, Type, overload + +# Type and type variable definitions +_T = TypeVar('_T') +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) +_Raisable = Union[BaseException, 'Type[BaseException]'] + +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +def chunked( + iterable: Iterable[_T], n: Optional[int], strict: bool = ... +) -> Iterator[List[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last( + iterable: Iterable[_T], n: int, default: _U +) -> Union[_T, _U]: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> List[_T]: ... + +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[object]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: ContextManager[Iterable[_T]], +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: Optional[_Raisable] = ..., + too_long: Optional[_Raisable] = ..., +) -> _T: ... +def raise_(exception: _Raisable, *args: Any) -> None: ... +def strictly_n( + iterable: Iterable[_T], + n: int, + too_short: Optional[_GenFn] = ..., + too_long: Optional[_GenFn] = ..., +) -> List[_T]: ... +def distinct_permutations( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Iterator[Tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[Union[_T, _U]]: ... +def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[Tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Optional[Callable[[object], object]] = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __iter__(self) -> Iterator[_U]: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> Tuple[List[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_evenly( + iterables: List[Iterable[_T]], lengths: Optional[List[int]] = ... +) -> Iterator[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: Optional[type] = ..., + levels: Optional[int] = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[List[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +def sliced( + seq: Sequence[_T], n: int, strict: bool = ... +) -> Iterator[Sequence[_T]]: ... +def split_at( + iterable: Iterable[_T], + pred: Callable[[_T], object], + maxsplit: int = ..., + keep_separator: bool = ..., +) -> Iterator[List[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_when( + iterable: Iterable[_T], + pred: Callable[[_T, _T], object], + maxsplit: int = ..., +) -> Iterator[List[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[Optional[int]] +) -> Iterator[List[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: Optional[int] = ..., + next_multiple: bool = ..., +) -> Iterator[Optional[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: Optional[int] = ..., + next_multiple: bool = ..., +) -> Iterator[Union[_T, _U]]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last( + iterable: Iterable[_T], default: _U +) -> Iterator[Union[_T, _U]]: ... +def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... + +class UnequalIterablesError(ValueError): + def __init__( + self, details: Optional[Tuple[int, int, int]] = ... + ) -> None: ... + +@overload +def zip_equal(__iter1: Iterable[_T1]) -> Iterator[Tuple[_T1]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], __iter2: Iterable[_T2] +) -> Iterator[Tuple[_T1, _T2]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], +) -> Iterator[Tuple[_T, ...]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[Tuple[Optional[_T1]]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[Tuple[Optional[_T1], Optional[_T2]]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[Tuple[Union[_T1, _U]]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[Tuple[Union[_T1, _U], Union[_T2, _U]]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + key: Optional[Callable[..., Any]] = ..., + reverse: bool = ..., +) -> List[Tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +def always_iterable( + obj: object, + base_type: Union[ + type, Tuple[Union[type, Tuple[Any, ...]], ...], None + ] = ..., +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[Tuple[bool, _T]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None = None, + valuefunc: None = None, + reducefunc: None = None, +) -> Iterator[Tuple[_T, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: None, +) -> Iterator[Tuple[_U, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterable[Tuple[_T, Iterable[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterable[Tuple[_U, Iterator[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterable[Tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterable[Tuple[_U, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterable[_V]], _W], +) -> Iterable[Tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterable[_V]], _W], +) -> Iterable[Tuple[_U, _W]]: ... + +class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): + @overload + def __init__(self, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... + def __bool__(self) -> bool: ... + def __contains__(self, elem: object) -> bool: ... + def __eq__(self, other: object) -> bool: ... + @overload + def __getitem__(self, key: int) -> _T: ... + @overload + def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __len__(self) -> int: ... + def __reduce__( + self, + ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... + def __repr__(self) -> str: ... + def __reversed__(self) -> Iterator[_T]: ... + def count(self, value: _T) -> int: ... + def index(self, value: _T) -> int: ... # type: ignore + +def count_cycle( + iterable: Iterable[_T], n: Optional[int] = ... +) -> Iterable[Tuple[int, _T]]: ... +def mark_ends( + iterable: Iterable[_T], +) -> Iterable[Tuple[bool, bool, _T]]: ... +def locate( + iterable: Iterable[object], + pred: Callable[..., Any] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... + +class islice_extended(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], *args: Optional[int] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + def __getitem__(self, index: slice) -> islice_extended[_T]: ... + +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ..., +) -> Iterator[Union[_T, _U]]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], maxlen: Optional[int] = ... + ) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> Dict[_U, List[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> Dict[_U, List[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[List[_T]], _W] = ..., +) -> Dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[List[_V]], _W], +) -> Dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: Optional[int] = ..., + window_size: int = ..., +) -> Iterator[Union[_T, _U]]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], k: Optional[int] = ... +) -> Iterator[List[List[_T]]]: ... + +class time_limited(Generic[_T], Iterator[_T]): + def __init__( + self, limit_seconds: float, iterable: Iterable[_T] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + +@overload +def only( + iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... +) -> Optional[_T]: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... +) -> Union[_T, _U]: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[Tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: Type[BaseException], +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: Type[BaseException], +) -> Iterator[_U]: ... +def map_if( + iterable: Iterable[Any], + pred: Callable[[Any], bool], + func: Callable[[Any], Any], + func_else: Optional[Callable[[Any], Any]] = ..., +) -> Iterator[Any]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Optional[Iterable[float]] = ..., +) -> List[_T]: ... +def is_sorted( + iterable: Iterable[_T], + key: Optional[Callable[[_T], _U]] = ..., + reverse: bool = False, + strict: bool = False, +) -> bool: ... + +class AbortThread(BaseException): + pass + +class callback_iter(Generic[_T], Iterator[_T]): + def __init__( + self, + func: Callable[..., Any], + callback_kwd: str = ..., + wait_seconds: float = ..., + ) -> None: ... + def __enter__(self) -> callback_iter[_T]: ... + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> Optional[bool]: ... + def __iter__(self) -> callback_iter[_T]: ... + def __next__(self) -> _T: ... + def _reader(self) -> Iterator[_T]: ... + @property + def done(self) -> bool: ... + @property + def result(self) -> Any: ... + +def windowed_complete( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[_T, ...]]: ... +def all_unique( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> bool: ... +def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... +def nth_permutation( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... +def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... +def combination_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def permutation_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def repeat_each(iterable: Iterable[_T], n: int = ...) -> Iterator[_T]: ... + +class countable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> countable[_T]: ... + def __next__(self) -> _T: ... + +def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[List[_T]]: ... +def zip_broadcast( + *objects: Union[_T, Iterable[_T]], + scalar_types: Union[ + type, Tuple[Union[type, Tuple[Any, ...]], ...], None + ] = ..., + strict: bool = ..., +) -> Iterable[Tuple[_T, ...]]: ... +def unique_in_window( + iterable: Iterable[_T], n: int, key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def duplicates_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def duplicates_justseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... + +class _SupportsLessThan(Protocol): + def __lt__(self, __other: Any) -> bool: ... + +_SupportsLessThanT = TypeVar("_SupportsLessThanT", bound=_SupportsLessThan) + +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], *, key: None = None +) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], *, key: Callable[[_T], _SupportsLessThan] +) -> Tuple[_T, _T]: ... +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], + *, + key: None = None, + default: _U, +) -> Union[_U, Tuple[_SupportsLessThanT, _SupportsLessThanT]]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], + *, + key: Callable[[_T], _SupportsLessThan], + default: _U, +) -> Union[_U, Tuple[_T, _T]]: ... +@overload +def minmax( + iterable_or_value: _SupportsLessThanT, + __other: _SupportsLessThanT, + *others: _SupportsLessThanT, +) -> Tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: _T, + __other: _T, + *others: _T, + key: Callable[[_T], _SupportsLessThan], +) -> Tuple[_T, _T]: ... +def longest_common_prefix( + iterables: Iterable[Iterable[_T]], +) -> Iterator[_T]: ... +def iequals(*iterables: Iterable[object]) -> bool: ... +def constrained_batches( + iterable: Iterable[object], + max_size: int, + max_count: Optional[int] = ..., + get_len: Callable[[_T], object] = ..., + strict: bool = ..., +) -> Iterator[Tuple[_T]]: ... diff --git a/libs/win/more_itertools/py.typed b/libs/win/more_itertools/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/more_itertools/recipes.py b/libs/win/more_itertools/recipes.py index 3a7706cb..85796207 100644 --- a/libs/win/more_itertools/recipes.py +++ b/libs/win/more_itertools/recipes.py @@ -7,20 +7,33 @@ Some backward-compatible usability improvements have been made. .. [1] http://docs.python.org/library/itertools.html#recipes """ -from collections import deque -from itertools import ( - chain, combinations, count, cycle, groupby, islice, repeat, starmap, tee -) +import math import operator + +from collections import deque +from collections.abc import Sized +from functools import reduce +from itertools import ( + chain, + combinations, + compress, + count, + cycle, + groupby, + islice, + repeat, + starmap, + tee, + zip_longest, +) from random import randrange, sample, choice -from six import PY2 -from six.moves import filter, filterfalse, map, range, zip, zip_longest - __all__ = [ - 'accumulate', 'all_equal', + 'batched', + 'before_and_after', 'consume', + 'convolve', 'dotproduct', 'first_true', 'flatten', @@ -30,8 +43,10 @@ __all__ = [ 'nth', 'nth_combination', 'padnone', + 'pad_none', 'pairwise', 'partition', + 'polynomial_from_roots', 'powerset', 'prepend', 'quantify', @@ -41,42 +56,18 @@ __all__ = [ 'random_product', 'repeatfunc', 'roundrobin', + 'sieve', + 'sliding_window', + 'subslices', 'tabulate', 'tail', 'take', + 'triplewise', 'unique_everseen', 'unique_justseen', ] - -def accumulate(iterable, func=operator.add): - """ - Return an iterator whose items are the accumulated results of a function - (specified by the optional *func* argument) that takes two arguments. - By default, returns accumulated sums with :func:`operator.add`. - - >>> list(accumulate([1, 2, 3, 4, 5])) # Running sum - [1, 3, 6, 10, 15] - >>> list(accumulate([1, 2, 3], func=operator.mul)) # Running product - [1, 2, 6] - >>> list(accumulate([0, 1, -1, 2, 3, 2], func=max)) # Running maximum - [0, 1, 1, 2, 3, 3] - - This function is available in the ``itertools`` module for Python 3.2 and - greater. - - """ - it = iter(iterable) - try: - total = next(it) - except StopIteration: - return - else: - yield total - - for element in it: - total = func(total, element) - yield total +_marker = object() def take(n, iterable): @@ -84,11 +75,12 @@ def take(n, iterable): >>> take(3, range(10)) [0, 1, 2] - >>> take(5, range(3)) - [0, 1, 2] - Effectively a short replacement for ``next`` based iterator consumption - when you want more than one item, but less than the whole iterator. + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] """ return list(islice(iterable, n)) @@ -115,12 +107,19 @@ def tabulate(function, start=0): def tail(n, iterable): """Return an iterator over the last *n* items of *iterable*. - >>> t = tail(3, 'ABCDEFG') - >>> list(t) - ['E', 'F', 'G'] + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] """ - return iter(deque(iterable, maxlen=n)) + # If the given iterable has a length, then we can use islice to get its + # final elements. Note that if the iterable is not actually Iterable, + # either islice or deque will throw a TypeError. This is why we don't + # check if it is Iterable. + if isinstance(iterable, Sized): + yield from islice(iterable, max(0, len(iterable) - n), None) + else: + yield from iter(deque(iterable, maxlen=n)) def consume(iterator, n=None): @@ -166,11 +165,11 @@ def consume(iterator, n=None): def nth(iterable, n, default=None): """Returns the nth item or a default value. - >>> l = range(10) - >>> nth(l, 3) - 3 - >>> nth(l, 20, "zebra") - 'zebra' + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' """ return next(islice(iterable, n, None), default) @@ -193,17 +192,17 @@ def all_equal(iterable): def quantify(iterable, pred=bool): """Return the how many times the predicate is true. - >>> quantify([True, False, True]) - 2 + >>> quantify([True, False, True]) + 2 """ return sum(map(pred, iterable)) -def padnone(iterable): +def pad_none(iterable): """Returns the sequence of elements and then returns ``None`` indefinitely. - >>> take(5, padnone(range(3))) + >>> take(5, pad_none(range(3))) [0, 1, 2, None, None] Useful for emulating the behavior of the built-in :func:`map` function. @@ -214,11 +213,14 @@ def padnone(iterable): return chain(iterable, repeat(None)) +padnone = pad_none + + def ncycles(iterable, n): """Returns the sequence elements *n* times - >>> list(ncycles(["a", "b"], 3)) - ['a', 'b', 'a', 'b', 'a', 'b'] + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] """ return chain.from_iterable(repeat(tuple(iterable), n)) @@ -227,8 +229,8 @@ def ncycles(iterable, n): def dotproduct(vec1, vec2): """Returns the dot product of the two iterables. - >>> dotproduct([10, 10], [20, 20]) - 400 + >>> dotproduct([10, 10], [20, 20]) + 400 """ return sum(map(operator.mul, vec1, vec2)) @@ -273,27 +275,109 @@ def repeatfunc(func, times=None, *args): return starmap(func, repeat(args, times)) -def pairwise(iterable): +def _pairwise(iterable): """Returns an iterator of paired items, overlapping, from the original - >>> take(4, pairwise(count())) - [(0, 1), (1, 2), (2, 3), (3, 4)] + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. """ a, b = tee(iterable) next(b, None) - return zip(a, b) + yield from zip(a, b) -def grouper(n, iterable, fillvalue=None): - """Collect data into fixed-length chunks or blocks. +try: + from itertools import pairwise as itertools_pairwise +except ImportError: + pairwise = _pairwise +else: - >>> list(grouper(3, 'ABCDEFG', 'x')) - [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + def pairwise(iterable): + yield from itertools_pairwise(iterable) + + pairwise.__doc__ = _pairwise.__doc__ + + +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def _zip_equal(*iterables): + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + break + else: + # If we didn't break out, we can use the built-in zip. + return zip(*iterables) + + # If we did break out, there was a mismatch. + raise UnequalIterablesError(details=(first_size, i, size)) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def grouper(iterable, n, incomplete='fill', fillvalue=None): + """Group elements from *iterable* into fixed-length groups of length *n*. + + >>> list(grouper('ABCDEF', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + The keyword arguments *incomplete* and *fillvalue* control what happens for + iterables whose length is not a multiple of *n*. + + When *incomplete* is `'fill'`, the last group will contain instances of + *fillvalue*. + + >>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + When *incomplete* is `'ignore'`, the last group will not be emitted. + + >>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised. + + >>> it = grouper('ABCDEFG', 3, incomplete='strict') + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnequalIterablesError """ args = [iter(iterable)] * n - return zip_longest(fillvalue=fillvalue, *args) + if incomplete == 'fill': + return zip_longest(*args, fillvalue=fillvalue) + if incomplete == 'strict': + return _zip_equal(*args) + if incomplete == 'ignore': + return zip(*args) + else: + raise ValueError('Expected fill, strict, or ignore') def roundrobin(*iterables): @@ -309,10 +393,7 @@ def roundrobin(*iterables): """ # Recipe credited to George Sakkis pending = len(iterables) - if PY2: - nexts = cycle(iter(it).next for it in iterables) - else: - nexts = cycle(iter(it).__next__ for it in iterables) + nexts = cycle(iter(it).__next__ for it in iterables) while pending: try: for next in nexts: @@ -334,18 +415,43 @@ def partition(pred, iterable): >>> list(even_items), list(odd_items) ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + """ - # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 - t1, t2 = tee(iterable) - return filterfalse(pred, t1), filter(pred, t2) + if pred is None: + pred = bool + + evaluations = ((pred(x), x) for x in iterable) + t1, t2 = tee(evaluations) + return ( + (x for (cond, x) in t1 if not cond), + (x for (cond, x) in t2 if cond), + ) def powerset(iterable): """Yields all possible subsets of the iterable. - >>> list(powerset([1,2,3])) + >>> list(powerset([1, 2, 3])) [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. Use :func:`unique_everseen` on the input to avoid generating + duplicates: + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + >>> from more_itertools import unique_everseen + >>> list(powerset(unique_everseen(seq))) + [(), (1,), (0,), (1, 0)] + """ s = list(iterable) return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) @@ -363,41 +469,46 @@ def unique_everseen(iterable, key=None): Sequences with a mix of hashable and unhashable items can be used. The function will be slower (i.e., `O(n^2)`) for unhashable items. + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similary, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + """ seenset = set() seenset_add = seenset.add seenlist = [] seenlist_add = seenlist.append - if key is None: - for element in iterable: - try: - if element not in seenset: - seenset_add(element) - yield element - except TypeError: - if element not in seenlist: - seenlist_add(element) - yield element - else: - for element in iterable: - k = key(element) - try: - if k not in seenset: - seenset_add(k) - yield element - except TypeError: - if k not in seenlist: - seenlist_add(k) - yield element + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element def unique_justseen(iterable, key=None): """Yields elements in order, ignoring serial duplicates - >>> list(unique_justseen('AAAABBBCCDAABBB')) - ['A', 'B', 'C', 'D', 'A', 'B'] - >>> list(unique_justseen('ABBCcAD', str.lower)) - ['A', 'B', 'C', 'A', 'D'] + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] """ return map(next, map(operator.itemgetter(1), groupby(iterable, key))) @@ -414,6 +525,16 @@ def iter_except(func, exception, first=None): >>> list(iter_except(l.pop, IndexError)) [2, 1, 0] + Multiple exceptions can be specified as a stopping condition: + + >>> l = [1, 2, 3, '...', 4, 5, 6] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [7, 6, 5] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [4, 3, 2] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [] + """ try: if first is not None: @@ -424,7 +545,7 @@ def iter_except(func, exception, first=None): pass -def first_true(iterable, default=False, pred=None): +def first_true(iterable, default=None, pred=None): """ Returns the first true value in the iterable. @@ -444,7 +565,7 @@ def first_true(iterable, default=False, pred=None): return next(filter(pred, iterable), default) -def random_product(*args, **kwds): +def random_product(*args, repeat=1): """Draw an item at random from each of the input iterables. >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP @@ -460,7 +581,7 @@ def random_product(*args, **kwds): ``itertools.product(*args, **kwarg)``. """ - pools = [tuple(pool) for pool in args] * kwds.get('repeat', 1) + pools = [tuple(pool) for pool in args] * repeat return tuple(choice(pool) for pool in pools) @@ -523,6 +644,12 @@ def nth_combination(iterable, r, index): sort position *index* directly, without computing the previous subsequences. + >>> nth_combination(range(5), 3, 5) + (0, 3, 4) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. """ pool = tuple(iterable) n = len(pool) @@ -559,7 +686,156 @@ def prepend(value, iterator): >>> list(prepend(value, iterator)) ['0', '1', '2', '3'] - To prepend multiple values, see :func:`itertools.chain`. + To prepend multiple values, see :func:`itertools.chain` + or :func:`value_chain`. """ return chain([value], iterator) + + +def convolve(signal, kernel): + """Convolve the iterable *signal* with the iterable *kernel*. + + >>> signal = (1, 2, 3, 4, 5) + >>> kernel = [3, 2, 1] + >>> list(convolve(signal, kernel)) + [3, 8, 14, 20, 26, 14, 5] + + Note: the input arguments are not interchangeable, as the *kernel* + is immediately consumed and stored. + + """ + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n - 1)): + window.append(x) + yield sum(map(operator.mul, kernel, window)) + + +def before_and_after(predicate, it): + """A variant of :func:`takewhile` that allows complete access to the + remainder of the iterator. + + >>> it = iter('ABCdEfGhI') + >>> all_upper, remainder = before_and_after(str.isupper, it) + >>> ''.join(all_upper) + 'ABC' + >>> ''.join(remainder) # takewhile() would lose the 'd' + 'dEfGhI' + + Note that the first iterator must be fully consumed before the second + iterator can generate valid results. + """ + it = iter(it) + transition = [] + + def true_iterator(): + for elem in it: + if predicate(elem): + yield elem + else: + transition.append(elem) + return + + # Note: this is different from itertools recipes to allow nesting + # before_and_after remainders into before_and_after again. See tests + # for an example. + remainder_iterator = chain(transition, it) + + return true_iterator(), remainder_iterator + + +def triplewise(iterable): + """Return overlapping triplets from *iterable*. + + >>> list(triplewise('ABCDE')) + [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E')] + + """ + for (a, _), (b, c) in pairwise(pairwise(iterable)): + yield a, b, c + + +def sliding_window(iterable, n): + """Return a sliding window of width *n* over *iterable*. + + >>> list(sliding_window(range(6), 4)) + [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)] + + If *iterable* has fewer than *n* items, then nothing is yielded: + + >>> list(sliding_window(range(3), 4)) + [] + + For a variant with more features, see :func:`windowed`. + """ + it = iter(iterable) + window = deque(islice(it, n), maxlen=n) + if len(window) == n: + yield tuple(window) + for x in it: + window.append(x) + yield tuple(window) + + +def subslices(iterable): + """Return all contiguous non-empty subslices of *iterable*. + + >>> list(subslices('ABC')) + [['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']] + + This is similar to :func:`substrings`, but emits items in a different + order. + """ + seq = list(iterable) + slices = starmap(slice, combinations(range(len(seq) + 1), 2)) + return map(operator.getitem, repeat(seq), slices) + + +def polynomial_from_roots(roots): + """Compute a polynomial's coefficients from its roots. + + >>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3) + >>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60 + [1, -4, -17, 60] + """ + # Use math.prod for Python 3.8+, + prod = getattr(math, 'prod', lambda x: reduce(operator.mul, x, 1)) + roots = list(map(operator.neg, roots)) + return [ + sum(map(prod, combinations(roots, k))) for k in range(len(roots) + 1) + ] + + +def sieve(n): + """Yield the primes less than n. + + >>> list(sieve(30)) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + """ + isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x))) + limit = isqrt(n) + 1 + data = bytearray([1]) * n + data[:2] = 0, 0 + for p in compress(range(limit), data): + data[p + p : n : p] = bytearray(len(range(p + p, n, p))) + + return compress(count(), data) + + +def batched(iterable, n): + """Batch data into lists of length *n*. The last batch may be shorter. + + >>> list(batched('ABCDEFG', 3)) + [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] + + This recipe is from the ``itertools`` docs. This library also provides + :func:`chunked`, which has a different implementation. + """ + it = iter(iterable) + while True: + batch = list(islice(it, n)) + if not batch: + break + yield batch diff --git a/libs/win/more_itertools/recipes.pyi b/libs/win/more_itertools/recipes.pyi new file mode 100644 index 00000000..29415c5a --- /dev/null +++ b/libs/win/more_itertools/recipes.pyi @@ -0,0 +1,110 @@ +"""Stubs for more_itertools.recipes""" +from typing import ( + Any, + Callable, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + TypeVar, + Union, +) +from typing_extensions import overload, Type + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + +def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... +def all_equal(iterable: Iterable[object]) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: Optional[int] = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... +def grouper( + iterable: Iterable[_T], + n: int, + incomplete: str = ..., + fillvalue: _U = ..., +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] +) -> Tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], + first: None = ..., +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], + first: Callable[[], _U], +) -> Iterator[Union[_T, _U]]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... +) -> Optional[_T]: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Optional[Callable[[_T], object]] = ..., +) -> Union[_T, _U]: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> Tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> Tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... +def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... +def before_and_after( + predicate: Callable[[_T], bool], it: Iterable[_T] +) -> Tuple[Iterator[_T], Iterator[_T]]: ... +def triplewise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T, _T]]: ... +def sliding_window( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[_T, ...]]: ... +def subslices(iterable: Iterable[_T]) -> Iterator[List[_T]]: ... +def polynomial_from_roots(roots: Sequence[int]) -> List[int]: ... +def sieve(n: int) -> Iterator[int]: ... +def batched( + iterable: Iterable[_T], + n: int, +) -> Iterator[List[_T]]: ... diff --git a/libs/win/more_itertools/tests/test_more.py b/libs/win/more_itertools/tests/test_more.py deleted file mode 100644 index a1b1e431..00000000 --- a/libs/win/more_itertools/tests/test_more.py +++ /dev/null @@ -1,2074 +0,0 @@ -from __future__ import division, print_function, unicode_literals - -from collections import OrderedDict -from decimal import Decimal -from doctest import DocTestSuite -from fractions import Fraction -from functools import partial, reduce -from heapq import merge -from io import StringIO -from itertools import ( - chain, - count, - groupby, - islice, - permutations, - product, - repeat, -) -from operator import add, mul, itemgetter -from unittest import TestCase - -from six.moves import filter, map, range, zip - -import more_itertools as mi - - -def load_tests(loader, tests, ignore): - # Add the doctests - tests.addTests(DocTestSuite('more_itertools.more')) - return tests - - -class CollateTests(TestCase): - """Unit tests for ``collate()``""" - # Also accidentally tests peekable, though that could use its own tests - - def test_default(self): - """Test with the default `key` function.""" - iterables = [range(4), range(7), range(3, 6)] - self.assertEqual( - sorted(reduce(list.__add__, [list(it) for it in iterables])), - list(mi.collate(*iterables)) - ) - - def test_key(self): - """Test using a custom `key` function.""" - iterables = [range(5, 0, -1), range(4, 0, -1)] - actual = sorted( - reduce(list.__add__, [list(it) for it in iterables]), reverse=True - ) - expected = list(mi.collate(*iterables, key=lambda x: -x)) - self.assertEqual(actual, expected) - - def test_empty(self): - """Be nice if passed an empty list of iterables.""" - self.assertEqual([], list(mi.collate())) - - def test_one(self): - """Work when only 1 iterable is passed.""" - self.assertEqual([0, 1], list(mi.collate(range(2)))) - - def test_reverse(self): - """Test the `reverse` kwarg.""" - iterables = [range(4, 0, -1), range(7, 0, -1), range(3, 6, -1)] - - actual = sorted( - reduce(list.__add__, [list(it) for it in iterables]), reverse=True - ) - expected = list(mi.collate(*iterables, reverse=True)) - self.assertEqual(actual, expected) - - def test_alias(self): - self.assertNotEqual(merge.__doc__, mi.collate.__doc__) - self.assertNotEqual(partial.__doc__, mi.collate.__doc__) - - -class ChunkedTests(TestCase): - """Tests for ``chunked()``""" - - def test_even(self): - """Test when ``n`` divides evenly into the length of the iterable.""" - self.assertEqual( - list(mi.chunked('ABCDEF', 3)), [['A', 'B', 'C'], ['D', 'E', 'F']] - ) - - def test_odd(self): - """Test when ``n`` does not divide evenly into the length of the - iterable. - - """ - self.assertEqual( - list(mi.chunked('ABCDE', 3)), [['A', 'B', 'C'], ['D', 'E']] - ) - - -class FirstTests(TestCase): - """Tests for ``first()``""" - - def test_many(self): - """Test that it works on many-item iterables.""" - # Also try it on a generator expression to make sure it works on - # whatever those return, across Python versions. - self.assertEqual(mi.first(x for x in range(4)), 0) - - def test_one(self): - """Test that it doesn't raise StopIteration prematurely.""" - self.assertEqual(mi.first([3]), 3) - - def test_empty_stop_iteration(self): - """It should raise StopIteration for empty iterables.""" - self.assertRaises(ValueError, lambda: mi.first([])) - - def test_default(self): - """It should return the provided default arg for empty iterables.""" - self.assertEqual(mi.first([], 'boo'), 'boo') - - -class IterOnlyRange: - """User-defined iterable class which only support __iter__. - - It is not specified to inherit ``object``, so indexing on a instance will - raise an ``AttributeError`` rather than ``TypeError`` in Python 2. - - >>> r = IterOnlyRange(5) - >>> r[0] - AttributeError: IterOnlyRange instance has no attribute '__getitem__' - - Note: In Python 3, ``TypeError`` will be raised because ``object`` is - inherited implicitly by default. - - >>> r[0] - TypeError: 'IterOnlyRange' object does not support indexing - """ - def __init__(self, n): - """Set the length of the range.""" - self.n = n - - def __iter__(self): - """Works same as range().""" - return iter(range(self.n)) - - -class LastTests(TestCase): - """Tests for ``last()``""" - - def test_many_nonsliceable(self): - """Test that it works on many-item non-slice-able iterables.""" - # Also try it on a generator expression to make sure it works on - # whatever those return, across Python versions. - self.assertEqual(mi.last(x for x in range(4)), 3) - - def test_one_nonsliceable(self): - """Test that it doesn't raise StopIteration prematurely.""" - self.assertEqual(mi.last(x for x in range(1)), 0) - - def test_empty_stop_iteration_nonsliceable(self): - """It should raise ValueError for empty non-slice-able iterables.""" - self.assertRaises(ValueError, lambda: mi.last(x for x in range(0))) - - def test_default_nonsliceable(self): - """It should return the provided default arg for empty non-slice-able - iterables. - """ - self.assertEqual(mi.last((x for x in range(0)), 'boo'), 'boo') - - def test_many_sliceable(self): - """Test that it works on many-item slice-able iterables.""" - self.assertEqual(mi.last([0, 1, 2, 3]), 3) - - def test_one_sliceable(self): - """Test that it doesn't raise StopIteration prematurely.""" - self.assertEqual(mi.last([3]), 3) - - def test_empty_stop_iteration_sliceable(self): - """It should raise ValueError for empty slice-able iterables.""" - self.assertRaises(ValueError, lambda: mi.last([])) - - def test_default_sliceable(self): - """It should return the provided default arg for empty slice-able - iterables. - """ - self.assertEqual(mi.last([], 'boo'), 'boo') - - def test_dict(self): - """last(dic) and last(dic.keys()) should return same result.""" - dic = {'a': 1, 'b': 2, 'c': 3} - self.assertEqual(mi.last(dic), mi.last(dic.keys())) - - def test_ordereddict(self): - """last(dic) should return the last key.""" - od = OrderedDict() - od['a'] = 1 - od['b'] = 2 - od['c'] = 3 - self.assertEqual(mi.last(od), 'c') - - def test_customrange(self): - """It should work on custom class where [] raises AttributeError.""" - self.assertEqual(mi.last(IterOnlyRange(5)), 4) - - -class PeekableTests(TestCase): - """Tests for ``peekable()`` behavor not incidentally covered by testing - ``collate()`` - - """ - def test_peek_default(self): - """Make sure passing a default into ``peek()`` works.""" - p = mi.peekable([]) - self.assertEqual(p.peek(7), 7) - - def test_truthiness(self): - """Make sure a ``peekable`` tests true iff there are items remaining in - the iterable. - - """ - p = mi.peekable([]) - self.assertFalse(p) - - p = mi.peekable(range(3)) - self.assertTrue(p) - - def test_simple_peeking(self): - """Make sure ``next`` and ``peek`` advance and don't advance the - iterator, respectively. - - """ - p = mi.peekable(range(10)) - self.assertEqual(next(p), 0) - self.assertEqual(p.peek(), 1) - self.assertEqual(next(p), 1) - - def test_indexing(self): - """ - Indexing into the peekable shouldn't advance the iterator. - """ - p = mi.peekable('abcdefghijkl') - - # The 0th index is what ``next()`` will return - self.assertEqual(p[0], 'a') - self.assertEqual(next(p), 'a') - - # Indexing further into the peekable shouldn't advance the itertor - self.assertEqual(p[2], 'd') - self.assertEqual(next(p), 'b') - - # The 0th index moves up with the iterator; the last index follows - self.assertEqual(p[0], 'c') - self.assertEqual(p[9], 'l') - - self.assertEqual(next(p), 'c') - self.assertEqual(p[8], 'l') - - # Negative indexing should work too - self.assertEqual(p[-2], 'k') - self.assertEqual(p[-9], 'd') - self.assertRaises(IndexError, lambda: p[-10]) - - def test_slicing(self): - """Slicing the peekable shouldn't advance the iterator.""" - seq = list('abcdefghijkl') - p = mi.peekable(seq) - - # Slicing the peekable should just be like slicing a re-iterable - self.assertEqual(p[1:4], seq[1:4]) - - # Advancing the iterator moves the slices up also - self.assertEqual(next(p), 'a') - self.assertEqual(p[1:4], seq[1:][1:4]) - - # Implicit starts and stop should work - self.assertEqual(p[:5], seq[1:][:5]) - self.assertEqual(p[:], seq[1:][:]) - - # Indexing past the end should work - self.assertEqual(p[:100], seq[1:][:100]) - - # Steps should work, including negative - self.assertEqual(p[::2], seq[1:][::2]) - self.assertEqual(p[::-1], seq[1:][::-1]) - - def test_slicing_reset(self): - """Test slicing on a fresh iterable each time""" - iterable = ['0', '1', '2', '3', '4', '5'] - indexes = list(range(-4, len(iterable) + 4)) + [None] - steps = [1, 2, 3, 4, -1, -2, -3, 4] - for slice_args in product(indexes, indexes, steps): - it = iter(iterable) - p = mi.peekable(it) - next(p) - index = slice(*slice_args) - actual = p[index] - expected = iterable[1:][index] - self.assertEqual(actual, expected, slice_args) - - def test_slicing_error(self): - iterable = '01234567' - p = mi.peekable(iter(iterable)) - - # Prime the cache - p.peek() - old_cache = list(p._cache) - - # Illegal slice - with self.assertRaises(ValueError): - p[1:-1:0] - - # Neither the cache nor the iteration should be affected - self.assertEqual(old_cache, list(p._cache)) - self.assertEqual(list(p), list(iterable)) - - def test_passthrough(self): - """Iterating a peekable without using ``peek()`` or ``prepend()`` - should just give the underlying iterable's elements (a trivial test but - useful to set a baseline in case something goes wrong)""" - expected = [1, 2, 3, 4, 5] - actual = list(mi.peekable(expected)) - self.assertEqual(actual, expected) - - # prepend() behavior tests - - def test_prepend(self): - """Tests intersperesed ``prepend()`` and ``next()`` calls""" - it = mi.peekable(range(2)) - actual = [] - - # Test prepend() before next() - it.prepend(10) - actual += [next(it), next(it)] - - # Test prepend() between next()s - it.prepend(11) - actual += [next(it), next(it)] - - # Test prepend() after source iterable is consumed - it.prepend(12) - actual += [next(it)] - - expected = [10, 0, 11, 1, 12] - self.assertEqual(actual, expected) - - def test_multi_prepend(self): - """Tests prepending multiple items and getting them in proper order""" - it = mi.peekable(range(5)) - actual = [next(it), next(it)] - it.prepend(10, 11, 12) - it.prepend(20, 21) - actual += list(it) - expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4] - self.assertEqual(actual, expected) - - def test_empty(self): - """Tests prepending in front of an empty iterable""" - it = mi.peekable([]) - it.prepend(10) - actual = list(it) - expected = [10] - self.assertEqual(actual, expected) - - def test_prepend_truthiness(self): - """Tests that ``__bool__()`` or ``__nonzero__()`` works properly - with ``prepend()``""" - it = mi.peekable(range(5)) - self.assertTrue(it) - actual = list(it) - self.assertFalse(it) - it.prepend(10) - self.assertTrue(it) - actual += [next(it)] - self.assertFalse(it) - expected = [0, 1, 2, 3, 4, 10] - self.assertEqual(actual, expected) - - def test_multi_prepend_peek(self): - """Tests prepending multiple elements and getting them in reverse order - while peeking""" - it = mi.peekable(range(5)) - actual = [next(it), next(it)] - self.assertEqual(it.peek(), 2) - it.prepend(10, 11, 12) - self.assertEqual(it.peek(), 10) - it.prepend(20, 21) - self.assertEqual(it.peek(), 20) - actual += list(it) - self.assertFalse(it) - expected = [0, 1, 20, 21, 10, 11, 12, 2, 3, 4] - self.assertEqual(actual, expected) - - def test_prepend_after_stop(self): - """Test resuming iteration after a previous exhaustion""" - it = mi.peekable(range(3)) - self.assertEqual(list(it), [0, 1, 2]) - self.assertRaises(StopIteration, lambda: next(it)) - it.prepend(10) - self.assertEqual(next(it), 10) - self.assertRaises(StopIteration, lambda: next(it)) - - def test_prepend_slicing(self): - """Tests interaction between prepending and slicing""" - seq = list(range(20)) - p = mi.peekable(seq) - - p.prepend(30, 40, 50) - pseq = [30, 40, 50] + seq # pseq for prepended_seq - - # adapt the specific tests from test_slicing - self.assertEqual(p[0], 30) - self.assertEqual(p[1:8], pseq[1:8]) - self.assertEqual(p[1:], pseq[1:]) - self.assertEqual(p[:5], pseq[:5]) - self.assertEqual(p[:], pseq[:]) - self.assertEqual(p[:100], pseq[:100]) - self.assertEqual(p[::2], pseq[::2]) - self.assertEqual(p[::-1], pseq[::-1]) - - def test_prepend_indexing(self): - """Tests interaction between prepending and indexing""" - seq = list(range(20)) - p = mi.peekable(seq) - - p.prepend(30, 40, 50) - - self.assertEqual(p[0], 30) - self.assertEqual(next(p), 30) - self.assertEqual(p[2], 0) - self.assertEqual(next(p), 40) - self.assertEqual(p[0], 50) - self.assertEqual(p[9], 8) - self.assertEqual(next(p), 50) - self.assertEqual(p[8], 8) - self.assertEqual(p[-2], 18) - self.assertEqual(p[-9], 11) - self.assertRaises(IndexError, lambda: p[-21]) - - def test_prepend_iterable(self): - """Tests prepending from an iterable""" - it = mi.peekable(range(5)) - # Don't directly use the range() object to avoid any range-specific - # optimizations - it.prepend(*(x for x in range(5))) - actual = list(it) - expected = list(chain(range(5), range(5))) - self.assertEqual(actual, expected) - - def test_prepend_many(self): - """Tests that prepending a huge number of elements works""" - it = mi.peekable(range(5)) - # Don't directly use the range() object to avoid any range-specific - # optimizations - it.prepend(*(x for x in range(20000))) - actual = list(it) - expected = list(chain(range(20000), range(5))) - self.assertEqual(actual, expected) - - def test_prepend_reversed(self): - """Tests prepending from a reversed iterable""" - it = mi.peekable(range(3)) - it.prepend(*reversed((10, 11, 12))) - actual = list(it) - expected = [12, 11, 10, 0, 1, 2] - self.assertEqual(actual, expected) - - -class ConsumerTests(TestCase): - """Tests for ``consumer()``""" - - def test_consumer(self): - @mi.consumer - def eater(): - while True: - x = yield # noqa - - e = eater() - e.send('hi') # without @consumer, would raise TypeError - - -class DistinctPermutationsTests(TestCase): - def test_distinct_permutations(self): - """Make sure the output for ``distinct_permutations()`` is the same as - set(permutations(it)). - - """ - iterable = ['z', 'a', 'a', 'q', 'q', 'q', 'y'] - test_output = sorted(mi.distinct_permutations(iterable)) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) - - def test_other_iterables(self): - """Make sure ``distinct_permutations()`` accepts a different type of - iterables. - - """ - # a generator - iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y']) - test_output = sorted(mi.distinct_permutations(iterable)) - # "reload" it - iterable = (c for c in ['z', 'a', 'a', 'q', 'q', 'q', 'y']) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) - - # an iterator - iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y']) - test_output = sorted(mi.distinct_permutations(iterable)) - # "reload" it - iterable = iter(['z', 'a', 'a', 'q', 'q', 'q', 'y']) - ref_output = sorted(set(permutations(iterable))) - self.assertEqual(test_output, ref_output) - - -class IlenTests(TestCase): - def test_ilen(self): - """Sanity-checks for ``ilen()``.""" - # Non-empty - self.assertEqual( - mi.ilen(filter(lambda x: x % 10 == 0, range(101))), 11 - ) - - # Empty - self.assertEqual(mi.ilen((x for x in range(0))), 0) - - # Iterable with __len__ - self.assertEqual(mi.ilen(list(range(6))), 6) - - -class WithIterTests(TestCase): - def test_with_iter(self): - s = StringIO('One fish\nTwo fish') - initial_words = [line.split()[0] for line in mi.with_iter(s)] - - # Iterable's items should be faithfully represented - self.assertEqual(initial_words, ['One', 'Two']) - # The file object should be closed - self.assertEqual(s.closed, True) - - -class OneTests(TestCase): - def test_basic(self): - it = iter(['item']) - self.assertEqual(mi.one(it), 'item') - - def test_too_short(self): - it = iter([]) - self.assertRaises(ValueError, lambda: mi.one(it)) - self.assertRaises(IndexError, lambda: mi.one(it, too_short=IndexError)) - - def test_too_long(self): - it = count() - self.assertRaises(ValueError, lambda: mi.one(it)) # burn 0 and 1 - self.assertEqual(next(it), 2) - self.assertRaises( - OverflowError, lambda: mi.one(it, too_long=OverflowError) - ) - - -class IntersperseTest(TestCase): - """ Tests for intersperse() """ - - def test_even(self): - iterable = (x for x in '01') - self.assertEqual( - list(mi.intersperse(None, iterable)), ['0', None, '1'] - ) - - def test_odd(self): - iterable = (x for x in '012') - self.assertEqual( - list(mi.intersperse(None, iterable)), ['0', None, '1', None, '2'] - ) - - def test_nested(self): - element = ('a', 'b') - iterable = (x for x in '012') - actual = list(mi.intersperse(element, iterable)) - expected = ['0', ('a', 'b'), '1', ('a', 'b'), '2'] - self.assertEqual(actual, expected) - - def test_not_iterable(self): - self.assertRaises(TypeError, lambda: mi.intersperse('x', 1)) - - def test_n(self): - for n, element, expected in [ - (1, '_', ['0', '_', '1', '_', '2', '_', '3', '_', '4', '_', '5']), - (2, '_', ['0', '1', '_', '2', '3', '_', '4', '5']), - (3, '_', ['0', '1', '2', '_', '3', '4', '5']), - (4, '_', ['0', '1', '2', '3', '_', '4', '5']), - (5, '_', ['0', '1', '2', '3', '4', '_', '5']), - (6, '_', ['0', '1', '2', '3', '4', '5']), - (7, '_', ['0', '1', '2', '3', '4', '5']), - (3, ['a', 'b'], ['0', '1', '2', ['a', 'b'], '3', '4', '5']), - ]: - iterable = (x for x in '012345') - actual = list(mi.intersperse(element, iterable, n=n)) - self.assertEqual(actual, expected) - - def test_n_zero(self): - self.assertRaises( - ValueError, lambda: list(mi.intersperse('x', '012', n=0)) - ) - - -class UniqueToEachTests(TestCase): - """Tests for ``unique_to_each()``""" - - def test_all_unique(self): - """When all the input iterables are unique the output should match - the input.""" - iterables = [[1, 2], [3, 4, 5], [6, 7, 8]] - self.assertEqual(mi.unique_to_each(*iterables), iterables) - - def test_duplicates(self): - """When there are duplicates in any of the input iterables that aren't - in the rest, those duplicates should be emitted.""" - iterables = ["mississippi", "missouri"] - self.assertEqual( - mi.unique_to_each(*iterables), [['p', 'p'], ['o', 'u', 'r']] - ) - - def test_mixed(self): - """When the input iterables contain different types the function should - still behave properly""" - iterables = ['x', (i for i in range(3)), [1, 2, 3], tuple()] - self.assertEqual(mi.unique_to_each(*iterables), [['x'], [0], [3], []]) - - -class WindowedTests(TestCase): - """Tests for ``windowed()``""" - - def test_basic(self): - actual = list(mi.windowed([1, 2, 3, 4, 5], 3)) - expected = [(1, 2, 3), (2, 3, 4), (3, 4, 5)] - self.assertEqual(actual, expected) - - def test_large_size(self): - """ - When the window size is larger than the iterable, and no fill value is - given,``None`` should be filled in. - """ - actual = list(mi.windowed([1, 2, 3, 4, 5], 6)) - expected = [(1, 2, 3, 4, 5, None)] - self.assertEqual(actual, expected) - - def test_fillvalue(self): - """ - When sizes don't match evenly, the given fill value should be used. - """ - iterable = [1, 2, 3, 4, 5] - - for n, kwargs, expected in [ - (6, {}, [(1, 2, 3, 4, 5, '!')]), # n > len(iterable) - (3, {'step': 3}, [(1, 2, 3), (4, 5, '!')]), # using ``step`` - ]: - actual = list(mi.windowed(iterable, n, fillvalue='!', **kwargs)) - self.assertEqual(actual, expected) - - def test_zero(self): - """When the window size is zero, an empty tuple should be emitted.""" - actual = list(mi.windowed([1, 2, 3, 4, 5], 0)) - expected = [tuple()] - self.assertEqual(actual, expected) - - def test_negative(self): - """When the window size is negative, ValueError should be raised.""" - with self.assertRaises(ValueError): - list(mi.windowed([1, 2, 3, 4, 5], -1)) - - def test_step(self): - """The window should advance by the number of steps provided""" - iterable = [1, 2, 3, 4, 5, 6, 7] - for n, step, expected in [ - (3, 2, [(1, 2, 3), (3, 4, 5), (5, 6, 7)]), # n > step - (3, 3, [(1, 2, 3), (4, 5, 6), (7, None, None)]), # n == step - (3, 4, [(1, 2, 3), (5, 6, 7)]), # line up nicely - (3, 5, [(1, 2, 3), (6, 7, None)]), # off by one - (3, 6, [(1, 2, 3), (7, None, None)]), # off by two - (3, 7, [(1, 2, 3)]), # step past the end - (7, 8, [(1, 2, 3, 4, 5, 6, 7)]), # step > len(iterable) - ]: - actual = list(mi.windowed(iterable, n, step=step)) - self.assertEqual(actual, expected) - - # Step must be greater than or equal to 1 - with self.assertRaises(ValueError): - list(mi.windowed(iterable, 3, step=0)) - - -class BucketTests(TestCase): - """Tests for ``bucket()``""" - - def test_basic(self): - iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33] - D = mi.bucket(iterable, key=lambda x: 10 * (x // 10)) - - # In-order access - self.assertEqual(list(D[10]), [10, 11, 12]) - - # Out of order access - self.assertEqual(list(D[30]), [30, 31, 33]) - self.assertEqual(list(D[20]), [20, 21, 22, 23]) - - self.assertEqual(list(D[40]), []) # Nothing in here! - - def test_in(self): - iterable = [10, 20, 30, 11, 21, 31, 12, 22, 23, 33] - D = mi.bucket(iterable, key=lambda x: 10 * (x // 10)) - - self.assertTrue(10 in D) - self.assertFalse(40 in D) - self.assertTrue(20 in D) - self.assertFalse(21 in D) - - # Checking in-ness shouldn't advance the iterator - self.assertEqual(next(D[10]), 10) - - def test_validator(self): - iterable = count(0) - key = lambda x: int(str(x)[0]) # First digit of each number - validator = lambda x: 0 < x < 10 # No leading zeros - D = mi.bucket(iterable, key, validator=validator) - self.assertEqual(mi.take(3, D[1]), [1, 10, 11]) - self.assertNotIn(0, D) # Non-valid entries don't return True - self.assertNotIn(0, D._cache) # Don't store non-valid entries - self.assertEqual(list(D[0]), []) - - -class SpyTests(TestCase): - """Tests for ``spy()``""" - - def test_basic(self): - original_iterable = iter('abcdefg') - head, new_iterable = mi.spy(original_iterable) - self.assertEqual(head, ['a']) - self.assertEqual( - list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - ) - - def test_unpacking(self): - original_iterable = iter('abcdefg') - (first, second, third), new_iterable = mi.spy(original_iterable, 3) - self.assertEqual(first, 'a') - self.assertEqual(second, 'b') - self.assertEqual(third, 'c') - self.assertEqual( - list(new_iterable), ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - ) - - def test_too_many(self): - original_iterable = iter('abc') - head, new_iterable = mi.spy(original_iterable, 4) - self.assertEqual(head, ['a', 'b', 'c']) - self.assertEqual(list(new_iterable), ['a', 'b', 'c']) - - def test_zero(self): - original_iterable = iter('abc') - head, new_iterable = mi.spy(original_iterable, 0) - self.assertEqual(head, []) - self.assertEqual(list(new_iterable), ['a', 'b', 'c']) - - -class InterleaveTests(TestCase): - def test_even(self): - actual = list(mi.interleave([1, 4, 7], [2, 5, 8], [3, 6, 9])) - expected = [1, 2, 3, 4, 5, 6, 7, 8, 9] - self.assertEqual(actual, expected) - - def test_short(self): - actual = list(mi.interleave([1, 4], [2, 5, 7], [3, 6, 8])) - expected = [1, 2, 3, 4, 5, 6] - self.assertEqual(actual, expected) - - def test_mixed_types(self): - it_list = ['a', 'b', 'c', 'd'] - it_str = '12345' - it_inf = count() - actual = list(mi.interleave(it_list, it_str, it_inf)) - expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', 3] - self.assertEqual(actual, expected) - - -class InterleaveLongestTests(TestCase): - def test_even(self): - actual = list(mi.interleave_longest([1, 4, 7], [2, 5, 8], [3, 6, 9])) - expected = [1, 2, 3, 4, 5, 6, 7, 8, 9] - self.assertEqual(actual, expected) - - def test_short(self): - actual = list(mi.interleave_longest([1, 4], [2, 5, 7], [3, 6, 8])) - expected = [1, 2, 3, 4, 5, 6, 7, 8] - self.assertEqual(actual, expected) - - def test_mixed_types(self): - it_list = ['a', 'b', 'c', 'd'] - it_str = '12345' - it_gen = (x for x in range(3)) - actual = list(mi.interleave_longest(it_list, it_str, it_gen)) - expected = ['a', '1', 0, 'b', '2', 1, 'c', '3', 2, 'd', '4', '5'] - self.assertEqual(actual, expected) - - -class TestCollapse(TestCase): - """Tests for ``collapse()``""" - - def test_collapse(self): - l = [[1], 2, [[3], 4], [[[5]]]] - self.assertEqual(list(mi.collapse(l)), [1, 2, 3, 4, 5]) - - def test_collapse_to_string(self): - l = [["s1"], "s2", [["s3"], "s4"], [[["s5"]]]] - self.assertEqual(list(mi.collapse(l)), ["s1", "s2", "s3", "s4", "s5"]) - - def test_collapse_flatten(self): - l = [[1], [2], [[3], 4], [[[5]]]] - self.assertEqual(list(mi.collapse(l, levels=1)), list(mi.flatten(l))) - - def test_collapse_to_level(self): - l = [[1], 2, [[3], 4], [[[5]]]] - self.assertEqual(list(mi.collapse(l, levels=2)), [1, 2, 3, 4, [5]]) - self.assertEqual( - list(mi.collapse(mi.collapse(l, levels=1), levels=1)), - list(mi.collapse(l, levels=2)) - ) - - def test_collapse_to_list(self): - l = (1, [2], (3, [4, (5,)], 'ab')) - actual = list(mi.collapse(l, base_type=list)) - expected = [1, [2], 3, [4, (5,)], 'ab'] - self.assertEqual(actual, expected) - - -class SideEffectTests(TestCase): - """Tests for ``side_effect()``""" - - def test_individual(self): - # The function increments the counter for each call - counter = [0] - - def func(arg): - counter[0] += 1 - - result = list(mi.side_effect(func, range(10))) - self.assertEqual(result, list(range(10))) - self.assertEqual(counter[0], 10) - - def test_chunked(self): - # The function increments the counter for each call - counter = [0] - - def func(arg): - counter[0] += 1 - - result = list(mi.side_effect(func, range(10), 2)) - self.assertEqual(result, list(range(10))) - self.assertEqual(counter[0], 5) - - def test_before_after(self): - f = StringIO() - collector = [] - - def func(item): - print(item, file=f) - collector.append(f.getvalue()) - - def it(): - yield u'a' - yield u'b' - raise RuntimeError('kaboom') - - before = lambda: print('HEADER', file=f) - after = f.close - - try: - mi.consume(mi.side_effect(func, it(), before=before, after=after)) - except RuntimeError: - pass - - # The iterable should have been written to the file - self.assertEqual(collector, [u'HEADER\na\n', u'HEADER\na\nb\n']) - - # The file should be closed even though something bad happened - self.assertTrue(f.closed) - - def test_before_fails(self): - f = StringIO() - func = lambda x: print(x, file=f) - - def before(): - raise RuntimeError('ouch') - - try: - mi.consume( - mi.side_effect(func, u'abc', before=before, after=f.close) - ) - except RuntimeError: - pass - - # The file should be closed even though something bad happened in the - # before function - self.assertTrue(f.closed) - - -class SlicedTests(TestCase): - """Tests for ``sliced()``""" - - def test_even(self): - """Test when the length of the sequence is divisible by *n*""" - seq = 'ABCDEFGHI' - self.assertEqual(list(mi.sliced(seq, 3)), ['ABC', 'DEF', 'GHI']) - - def test_odd(self): - """Test when the length of the sequence is not divisible by *n*""" - seq = 'ABCDEFGHI' - self.assertEqual(list(mi.sliced(seq, 4)), ['ABCD', 'EFGH', 'I']) - - def test_not_sliceable(self): - seq = (x for x in 'ABCDEFGHI') - - with self.assertRaises(TypeError): - list(mi.sliced(seq, 3)) - - -class SplitAtTests(TestCase): - """Tests for ``split()``""" - - def comp_with_str_split(self, str_to_split, delim): - pred = lambda c: c == delim - actual = list(map(''.join, mi.split_at(str_to_split, pred))) - expected = str_to_split.split(delim) - self.assertEqual(actual, expected) - - def test_seperators(self): - test_strs = ['', 'abcba', 'aaabbbcccddd', 'e'] - for s, delim in product(test_strs, 'abcd'): - self.comp_with_str_split(s, delim) - - -class SplitBeforeTest(TestCase): - """Tests for ``split_before()``""" - - def test_starts_with_sep(self): - actual = list(mi.split_before('xooxoo', lambda c: c == 'x')) - expected = [['x', 'o', 'o'], ['x', 'o', 'o']] - self.assertEqual(actual, expected) - - def test_ends_with_sep(self): - actual = list(mi.split_before('ooxoox', lambda c: c == 'x')) - expected = [['o', 'o'], ['x', 'o', 'o'], ['x']] - self.assertEqual(actual, expected) - - def test_no_sep(self): - actual = list(mi.split_before('ooo', lambda c: c == 'x')) - expected = [['o', 'o', 'o']] - self.assertEqual(actual, expected) - - -class SplitAfterTest(TestCase): - """Tests for ``split_after()``""" - - def test_starts_with_sep(self): - actual = list(mi.split_after('xooxoo', lambda c: c == 'x')) - expected = [['x'], ['o', 'o', 'x'], ['o', 'o']] - self.assertEqual(actual, expected) - - def test_ends_with_sep(self): - actual = list(mi.split_after('ooxoox', lambda c: c == 'x')) - expected = [['o', 'o', 'x'], ['o', 'o', 'x']] - self.assertEqual(actual, expected) - - def test_no_sep(self): - actual = list(mi.split_after('ooo', lambda c: c == 'x')) - expected = [['o', 'o', 'o']] - self.assertEqual(actual, expected) - - -class PaddedTest(TestCase): - """Tests for ``padded()``""" - - def test_no_n(self): - seq = [1, 2, 3] - - # No fillvalue - self.assertEqual(mi.take(5, mi.padded(seq)), [1, 2, 3, None, None]) - - # With fillvalue - self.assertEqual( - mi.take(5, mi.padded(seq, fillvalue='')), [1, 2, 3, '', ''] - ) - - def test_invalid_n(self): - self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=-1))) - self.assertRaises(ValueError, lambda: list(mi.padded([1, 2, 3], n=0))) - - def test_valid_n(self): - seq = [1, 2, 3, 4, 5] - - # No need for padding: len(seq) <= n - self.assertEqual(list(mi.padded(seq, n=4)), [1, 2, 3, 4, 5]) - self.assertEqual(list(mi.padded(seq, n=5)), [1, 2, 3, 4, 5]) - - # No fillvalue - self.assertEqual( - list(mi.padded(seq, n=7)), [1, 2, 3, 4, 5, None, None] - ) - - # With fillvalue - self.assertEqual( - list(mi.padded(seq, fillvalue='', n=7)), [1, 2, 3, 4, 5, '', ''] - ) - - def test_next_multiple(self): - seq = [1, 2, 3, 4, 5, 6] - - # No need for padding: len(seq) % n == 0 - self.assertEqual( - list(mi.padded(seq, n=3, next_multiple=True)), [1, 2, 3, 4, 5, 6] - ) - - # Padding needed: len(seq) < n - self.assertEqual( - list(mi.padded(seq, n=8, next_multiple=True)), - [1, 2, 3, 4, 5, 6, None, None] - ) - - # No padding needed: len(seq) == n - self.assertEqual( - list(mi.padded(seq, n=6, next_multiple=True)), [1, 2, 3, 4, 5, 6] - ) - - # Padding needed: len(seq) > n - self.assertEqual( - list(mi.padded(seq, n=4, next_multiple=True)), - [1, 2, 3, 4, 5, 6, None, None] - ) - - # With fillvalue - self.assertEqual( - list(mi.padded(seq, fillvalue='', n=4, next_multiple=True)), - [1, 2, 3, 4, 5, 6, '', ''] - ) - - -class DistributeTest(TestCase): - """Tests for distribute()""" - - def test_invalid_n(self): - self.assertRaises(ValueError, lambda: mi.distribute(-1, [1, 2, 3])) - self.assertRaises(ValueError, lambda: mi.distribute(0, [1, 2, 3])) - - def test_basic(self): - iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - for n, expected in [ - (1, [iterable]), - (2, [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]), - (3, [[1, 4, 7, 10], [2, 5, 8], [3, 6, 9]]), - (10, [[n] for n in range(1, 10 + 1)]), - ]: - self.assertEqual( - [list(x) for x in mi.distribute(n, iterable)], expected - ) - - def test_large_n(self): - iterable = [1, 2, 3, 4] - self.assertEqual( - [list(x) for x in mi.distribute(6, iterable)], - [[1], [2], [3], [4], [], []] - ) - - -class StaggerTest(TestCase): - """Tests for ``stagger()``""" - - def test_default(self): - iterable = [0, 1, 2, 3] - actual = list(mi.stagger(iterable)) - expected = [(None, 0, 1), (0, 1, 2), (1, 2, 3)] - self.assertEqual(actual, expected) - - def test_offsets(self): - iterable = [0, 1, 2, 3] - for offsets, expected in [ - ((-2, 0, 2), [('', 0, 2), ('', 1, 3)]), - ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3)]), - ((1, 2), [(1, 2), (2, 3)]), - ]: - all_groups = mi.stagger(iterable, offsets=offsets, fillvalue='') - self.assertEqual(list(all_groups), expected) - - def test_longest(self): - iterable = [0, 1, 2, 3] - for offsets, expected in [ - ( - (-1, 0, 1), - [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, ''), (3, '', '')] - ), - ((-2, -1), [('', ''), ('', 0), (0, 1), (1, 2), (2, 3), (3, '')]), - ((1, 2), [(1, 2), (2, 3), (3, '')]), - ]: - all_groups = mi.stagger( - iterable, offsets=offsets, fillvalue='', longest=True - ) - self.assertEqual(list(all_groups), expected) - - -class ZipOffsetTest(TestCase): - """Tests for ``zip_offset()``""" - - def test_shortest(self): - a_1 = [0, 1, 2, 3] - a_2 = [0, 1, 2, 3, 4, 5] - a_3 = [0, 1, 2, 3, 4, 5, 6, 7] - actual = list( - mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), fillvalue='') - ) - expected = [('', 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)] - self.assertEqual(actual, expected) - - def test_longest(self): - a_1 = [0, 1, 2, 3] - a_2 = [0, 1, 2, 3, 4, 5] - a_3 = [0, 1, 2, 3, 4, 5, 6, 7] - actual = list( - mi.zip_offset(a_1, a_2, a_3, offsets=(-1, 0, 1), longest=True) - ) - expected = [ - (None, 0, 1), - (0, 1, 2), - (1, 2, 3), - (2, 3, 4), - (3, 4, 5), - (None, 5, 6), - (None, None, 7), - ] - self.assertEqual(actual, expected) - - def test_mismatch(self): - iterables = [0, 1, 2], [2, 3, 4] - offsets = (-1, 0, 1) - self.assertRaises( - ValueError, - lambda: list(mi.zip_offset(*iterables, offsets=offsets)) - ) - - -class SortTogetherTest(TestCase): - """Tests for sort_together()""" - - def test_key_list(self): - """tests `key_list` including default, iterables include duplicates""" - iterables = [ - ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'], - ['May', 'Aug.', 'May', 'June', 'July', 'July'], - [97, 20, 100, 70, 100, 20] - ] - - self.assertEqual( - mi.sort_together(iterables), - [ - ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'), - ('June', 'July', 'July', 'May', 'Aug.', 'May'), - (70, 100, 20, 97, 20, 100) - ] - ) - - self.assertEqual( - mi.sort_together(iterables, key_list=(0, 1)), - [ - ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'), - ('July', 'July', 'June', 'Aug.', 'May', 'May'), - (100, 20, 70, 20, 97, 100) - ] - ) - - self.assertEqual( - mi.sort_together(iterables, key_list=(0, 1, 2)), - [ - ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'), - ('July', 'July', 'June', 'Aug.', 'May', 'May'), - (20, 100, 70, 20, 97, 100) - ] - ) - - self.assertEqual( - mi.sort_together(iterables, key_list=(2,)), - [ - ('GA', 'CT', 'CT', 'GA', 'GA', 'CT'), - ('Aug.', 'July', 'June', 'May', 'May', 'July'), - (20, 20, 70, 97, 100, 100) - ] - ) - - def test_invalid_key_list(self): - """tests `key_list` for indexes not available in `iterables`""" - iterables = [ - ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'], - ['May', 'Aug.', 'May', 'June', 'July', 'July'], - [97, 20, 100, 70, 100, 20] - ] - - self.assertRaises( - IndexError, lambda: mi.sort_together(iterables, key_list=(5,)) - ) - - def test_reverse(self): - """tests `reverse` to ensure a reverse sort for `key_list` iterables""" - iterables = [ - ['GA', 'GA', 'GA', 'CT', 'CT', 'CT'], - ['May', 'Aug.', 'May', 'June', 'July', 'July'], - [97, 20, 100, 70, 100, 20] - ] - - self.assertEqual( - mi.sort_together(iterables, key_list=(0, 1, 2), reverse=True), - [('GA', 'GA', 'GA', 'CT', 'CT', 'CT'), - ('May', 'May', 'Aug.', 'June', 'July', 'July'), - (100, 97, 20, 70, 100, 20)] - ) - - def test_uneven_iterables(self): - """tests trimming of iterables to the shortest length before sorting""" - iterables = [['GA', 'GA', 'GA', 'CT', 'CT', 'CT', 'MA'], - ['May', 'Aug.', 'May', 'June', 'July', 'July'], - [97, 20, 100, 70, 100, 20, 0]] - - self.assertEqual( - mi.sort_together(iterables), - [ - ('CT', 'CT', 'CT', 'GA', 'GA', 'GA'), - ('June', 'July', 'July', 'May', 'Aug.', 'May'), - (70, 100, 20, 97, 20, 100) - ] - ) - - -class DivideTest(TestCase): - """Tests for divide()""" - - def test_invalid_n(self): - self.assertRaises(ValueError, lambda: mi.divide(-1, [1, 2, 3])) - self.assertRaises(ValueError, lambda: mi.divide(0, [1, 2, 3])) - - def test_basic(self): - iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - for n, expected in [ - (1, [iterable]), - (2, [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]), - (3, [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]), - (10, [[n] for n in range(1, 10 + 1)]), - ]: - self.assertEqual( - [list(x) for x in mi.divide(n, iterable)], expected - ) - - def test_large_n(self): - iterable = [1, 2, 3, 4] - self.assertEqual( - [list(x) for x in mi.divide(6, iterable)], - [[1], [2], [3], [4], [], []] - ) - - -class TestAlwaysIterable(TestCase): - """Tests for always_iterable()""" - def test_single(self): - self.assertEqual(list(mi.always_iterable(1)), [1]) - - def test_strings(self): - for obj in ['foo', b'bar', u'baz']: - actual = list(mi.always_iterable(obj)) - expected = [obj] - self.assertEqual(actual, expected) - - def test_base_type(self): - dict_obj = {'a': 1, 'b': 2} - str_obj = '123' - - # Default: dicts are iterable like they normally are - default_actual = list(mi.always_iterable(dict_obj)) - default_expected = list(dict_obj) - self.assertEqual(default_actual, default_expected) - - # Unitary types set: dicts are not iterable - custom_actual = list(mi.always_iterable(dict_obj, base_type=dict)) - custom_expected = [dict_obj] - self.assertEqual(custom_actual, custom_expected) - - # With unitary types set, strings are iterable - str_actual = list(mi.always_iterable(str_obj, base_type=None)) - str_expected = list(str_obj) - self.assertEqual(str_actual, str_expected) - - def test_iterables(self): - self.assertEqual(list(mi.always_iterable([0, 1])), [0, 1]) - self.assertEqual( - list(mi.always_iterable([0, 1], base_type=list)), [[0, 1]] - ) - self.assertEqual( - list(mi.always_iterable(iter('foo'))), ['f', 'o', 'o'] - ) - self.assertEqual(list(mi.always_iterable([])), []) - - def test_none(self): - self.assertEqual(list(mi.always_iterable(None)), []) - - def test_generator(self): - def _gen(): - yield 0 - yield 1 - - self.assertEqual(list(mi.always_iterable(_gen())), [0, 1]) - - -class AdjacentTests(TestCase): - def test_typical(self): - actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10))) - expected = [(True, 0), (True, 1), (False, 2), (False, 3), (True, 4), - (True, 5), (True, 6), (False, 7), (False, 8), (False, 9)] - self.assertEqual(actual, expected) - - def test_empty_iterable(self): - actual = list(mi.adjacent(lambda x: x % 5 == 0, [])) - expected = [] - self.assertEqual(actual, expected) - - def test_length_one(self): - actual = list(mi.adjacent(lambda x: x % 5 == 0, [0])) - expected = [(True, 0)] - self.assertEqual(actual, expected) - - actual = list(mi.adjacent(lambda x: x % 5 == 0, [1])) - expected = [(False, 1)] - self.assertEqual(actual, expected) - - def test_consecutive_true(self): - """Test that when the predicate matches multiple consecutive elements - it doesn't repeat elements in the output""" - actual = list(mi.adjacent(lambda x: x % 5 < 2, range(10))) - expected = [(True, 0), (True, 1), (True, 2), (False, 3), (True, 4), - (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)] - self.assertEqual(actual, expected) - - def test_distance(self): - actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=2)) - expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4), - (True, 5), (True, 6), (True, 7), (False, 8), (False, 9)] - self.assertEqual(actual, expected) - - actual = list(mi.adjacent(lambda x: x % 5 == 0, range(10), distance=3)) - expected = [(True, 0), (True, 1), (True, 2), (True, 3), (True, 4), - (True, 5), (True, 6), (True, 7), (True, 8), (False, 9)] - self.assertEqual(actual, expected) - - def test_large_distance(self): - """Test distance larger than the length of the iterable""" - iterable = range(10) - actual = list(mi.adjacent(lambda x: x % 5 == 4, iterable, distance=20)) - expected = list(zip(repeat(True), iterable)) - self.assertEqual(actual, expected) - - actual = list(mi.adjacent(lambda x: False, iterable, distance=20)) - expected = list(zip(repeat(False), iterable)) - self.assertEqual(actual, expected) - - def test_zero_distance(self): - """Test that adjacent() reduces to zip+map when distance is 0""" - iterable = range(1000) - predicate = lambda x: x % 4 == 2 - actual = mi.adjacent(predicate, iterable, 0) - expected = zip(map(predicate, iterable), iterable) - self.assertTrue(all(a == e for a, e in zip(actual, expected))) - - def test_negative_distance(self): - """Test that adjacent() raises an error with negative distance""" - pred = lambda x: x - self.assertRaises( - ValueError, lambda: mi.adjacent(pred, range(1000), -1) - ) - self.assertRaises( - ValueError, lambda: mi.adjacent(pred, range(10), -10) - ) - - def test_grouping(self): - """Test interaction of adjacent() with groupby_transform()""" - iterable = mi.adjacent(lambda x: x % 5 == 0, range(10)) - grouper = mi.groupby_transform(iterable, itemgetter(0), itemgetter(1)) - actual = [(k, list(g)) for k, g in grouper] - expected = [ - (True, [0, 1]), - (False, [2, 3]), - (True, [4, 5, 6]), - (False, [7, 8, 9]), - ] - self.assertEqual(actual, expected) - - def test_call_once(self): - """Test that the predicate is only called once per item.""" - already_seen = set() - iterable = range(10) - - def predicate(item): - self.assertNotIn(item, already_seen) - already_seen.add(item) - return True - - actual = list(mi.adjacent(predicate, iterable)) - expected = [(True, x) for x in iterable] - self.assertEqual(actual, expected) - - -class GroupByTransformTests(TestCase): - def assertAllGroupsEqual(self, groupby1, groupby2): - """Compare two groupby objects for equality, both keys and groups.""" - for a, b in zip(groupby1, groupby2): - key1, group1 = a - key2, group2 = b - self.assertEqual(key1, key2) - self.assertListEqual(list(group1), list(group2)) - self.assertRaises(StopIteration, lambda: next(groupby1)) - self.assertRaises(StopIteration, lambda: next(groupby2)) - - def test_default_funcs(self): - """Test that groupby_transform() with default args mimics groupby()""" - iterable = [(x // 5, x) for x in range(1000)] - actual = mi.groupby_transform(iterable) - expected = groupby(iterable) - self.assertAllGroupsEqual(actual, expected) - - def test_valuefunc(self): - iterable = [(int(x / 5), int(x / 3), x) for x in range(10)] - - # Test the standard usage of grouping one iterable using another's keys - grouper = mi.groupby_transform( - iterable, keyfunc=itemgetter(0), valuefunc=itemgetter(-1) - ) - actual = [(k, list(g)) for k, g in grouper] - expected = [(0, [0, 1, 2, 3, 4]), (1, [5, 6, 7, 8, 9])] - self.assertEqual(actual, expected) - - grouper = mi.groupby_transform( - iterable, keyfunc=itemgetter(1), valuefunc=itemgetter(-1) - ) - actual = [(k, list(g)) for k, g in grouper] - expected = [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])] - self.assertEqual(actual, expected) - - # and now for something a little different - d = dict(zip(range(10), 'abcdefghij')) - grouper = mi.groupby_transform( - range(10), keyfunc=lambda x: x // 5, valuefunc=d.get - ) - actual = [(k, ''.join(g)) for k, g in grouper] - expected = [(0, 'abcde'), (1, 'fghij')] - self.assertEqual(actual, expected) - - def test_no_valuefunc(self): - iterable = range(1000) - - def key(x): - return x // 5 - - actual = mi.groupby_transform(iterable, key, valuefunc=None) - expected = groupby(iterable, key) - self.assertAllGroupsEqual(actual, expected) - - actual = mi.groupby_transform(iterable, key) # default valuefunc - expected = groupby(iterable, key) - self.assertAllGroupsEqual(actual, expected) - - -class NumericRangeTests(TestCase): - def test_basic(self): - for args, expected in [ - ((4,), [0, 1, 2, 3]), - ((4.0,), [0.0, 1.0, 2.0, 3.0]), - ((1.0, 4), [1.0, 2.0, 3.0]), - ((1, 4.0), [1, 2, 3]), - ((1.0, 5), [1.0, 2.0, 3.0, 4.0]), - ((0, 20, 5), [0, 5, 10, 15]), - ((0, 20, 5.0), [0.0, 5.0, 10.0, 15.0]), - ((0, 10, 3), [0, 3, 6, 9]), - ((0, 10, 3.0), [0.0, 3.0, 6.0, 9.0]), - ((0, -5, -1), [0, -1, -2, -3, -4]), - ((0.0, -5, -1), [0.0, -1.0, -2.0, -3.0, -4.0]), - ((1, 2, Fraction(1, 2)), [Fraction(1, 1), Fraction(3, 2)]), - ((0,), []), - ((0.0,), []), - ((1, 0), []), - ((1.0, 0.0), []), - ((Fraction(2, 1),), [Fraction(0, 1), Fraction(1, 1)]), - ((Decimal('2.0'),), [Decimal('0.0'), Decimal('1.0')]), - ]: - actual = list(mi.numeric_range(*args)) - self.assertEqual(actual, expected) - self.assertTrue( - all(type(a) == type(e) for a, e in zip(actual, expected)) - ) - - def test_arg_count(self): - self.assertRaises(TypeError, lambda: list(mi.numeric_range())) - self.assertRaises( - TypeError, lambda: list(mi.numeric_range(0, 1, 2, 3)) - ) - - def test_zero_step(self): - self.assertRaises( - ValueError, lambda: list(mi.numeric_range(1, 2, 0)) - ) - - -class CountCycleTests(TestCase): - def test_basic(self): - expected = [ - (0, 'a'), (0, 'b'), (0, 'c'), - (1, 'a'), (1, 'b'), (1, 'c'), - (2, 'a'), (2, 'b'), (2, 'c'), - ] - for actual in [ - mi.take(9, mi.count_cycle('abc')), # n=None - list(mi.count_cycle('abc', 3)), # n=3 - ]: - self.assertEqual(actual, expected) - - def test_empty(self): - self.assertEqual(list(mi.count_cycle('')), []) - self.assertEqual(list(mi.count_cycle('', 2)), []) - - def test_negative(self): - self.assertEqual(list(mi.count_cycle('abc', -3)), []) - - -class LocateTests(TestCase): - def test_default_pred(self): - iterable = [0, 1, 1, 0, 1, 0, 0] - actual = list(mi.locate(iterable)) - expected = [1, 2, 4] - self.assertEqual(actual, expected) - - def test_no_matches(self): - iterable = [0, 0, 0] - actual = list(mi.locate(iterable)) - expected = [] - self.assertEqual(actual, expected) - - def test_custom_pred(self): - iterable = ['0', 1, 1, '0', 1, '0', '0'] - pred = lambda x: x == '0' - actual = list(mi.locate(iterable, pred)) - expected = [0, 3, 5, 6] - self.assertEqual(actual, expected) - - def test_window_size(self): - iterable = ['0', 1, 1, '0', 1, '0', '0'] - pred = lambda *args: args == ('0', 1) - actual = list(mi.locate(iterable, pred, window_size=2)) - expected = [0, 3] - self.assertEqual(actual, expected) - - def test_window_size_large(self): - iterable = [1, 2, 3, 4] - pred = lambda a, b, c, d, e: True - actual = list(mi.locate(iterable, pred, window_size=5)) - expected = [0] - self.assertEqual(actual, expected) - - def test_window_size_zero(self): - iterable = [1, 2, 3, 4] - pred = lambda: True - with self.assertRaises(ValueError): - list(mi.locate(iterable, pred, window_size=0)) - - -class StripFunctionTests(TestCase): - def test_hashable(self): - iterable = list('www.example.com') - pred = lambda x: x in set('cmowz.') - - self.assertEqual(list(mi.lstrip(iterable, pred)), list('example.com')) - self.assertEqual(list(mi.rstrip(iterable, pred)), list('www.example')) - self.assertEqual(list(mi.strip(iterable, pred)), list('example')) - - def test_not_hashable(self): - iterable = [ - list('http://'), list('www'), list('.example'), list('.com') - ] - pred = lambda x: x in [list('http://'), list('www'), list('.com')] - - self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[2:]) - self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:3]) - self.assertEqual(list(mi.strip(iterable, pred)), iterable[2: 3]) - - def test_math(self): - iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2] - pred = lambda x: x <= 2 - - self.assertEqual(list(mi.lstrip(iterable, pred)), iterable[3:]) - self.assertEqual(list(mi.rstrip(iterable, pred)), iterable[:-3]) - self.assertEqual(list(mi.strip(iterable, pred)), iterable[3:-3]) - - -class IsliceExtendedTests(TestCase): - def test_all(self): - iterable = ['0', '1', '2', '3', '4', '5'] - indexes = list(range(-4, len(iterable) + 4)) + [None] - steps = [1, 2, 3, 4, -1, -2, -3, 4] - for slice_args in product(indexes, indexes, steps): - try: - actual = list(mi.islice_extended(iterable, *slice_args)) - except Exception as e: - self.fail((slice_args, e)) - - expected = iterable[slice(*slice_args)] - self.assertEqual(actual, expected, slice_args) - - def test_zero_step(self): - with self.assertRaises(ValueError): - list(mi.islice_extended([1, 2, 3], 0, 1, 0)) - - -class ConsecutiveGroupsTest(TestCase): - def test_numbers(self): - iterable = [-10, -8, -7, -6, 1, 2, 4, 5, -1, 7] - actual = [list(g) for g in mi.consecutive_groups(iterable)] - expected = [[-10], [-8, -7, -6], [1, 2], [4, 5], [-1], [7]] - self.assertEqual(actual, expected) - - def test_custom_ordering(self): - iterable = ['1', '10', '11', '20', '21', '22', '30', '31'] - ordering = lambda x: int(x) - actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)] - expected = [['1'], ['10', '11'], ['20', '21', '22'], ['30', '31']] - self.assertEqual(actual, expected) - - def test_exotic_ordering(self): - iterable = [ - ('a', 'b', 'c', 'd'), - ('a', 'c', 'b', 'd'), - ('a', 'c', 'd', 'b'), - ('a', 'd', 'b', 'c'), - ('d', 'b', 'c', 'a'), - ('d', 'c', 'a', 'b'), - ] - ordering = list(permutations('abcd')).index - actual = [list(g) for g in mi.consecutive_groups(iterable, ordering)] - expected = [ - [('a', 'b', 'c', 'd')], - [('a', 'c', 'b', 'd'), ('a', 'c', 'd', 'b'), ('a', 'd', 'b', 'c')], - [('d', 'b', 'c', 'a'), ('d', 'c', 'a', 'b')], - ] - self.assertEqual(actual, expected) - - -class DifferenceTest(TestCase): - def test_normal(self): - iterable = [10, 20, 30, 40, 50] - actual = list(mi.difference(iterable)) - expected = [10, 10, 10, 10, 10] - self.assertEqual(actual, expected) - - def test_custom(self): - iterable = [10, 20, 30, 40, 50] - actual = list(mi.difference(iterable, add)) - expected = [10, 30, 50, 70, 90] - self.assertEqual(actual, expected) - - def test_roundtrip(self): - original = list(range(100)) - accumulated = mi.accumulate(original) - actual = list(mi.difference(accumulated)) - self.assertEqual(actual, original) - - def test_one(self): - self.assertEqual(list(mi.difference([0])), [0]) - - def test_empty(self): - self.assertEqual(list(mi.difference([])), []) - - -class SeekableTest(TestCase): - def test_exhaustion_reset(self): - iterable = [str(n) for n in range(10)] - - s = mi.seekable(iterable) - self.assertEqual(list(s), iterable) # Normal iteration - self.assertEqual(list(s), []) # Iterable is exhausted - - s.seek(0) - self.assertEqual(list(s), iterable) # Back in action - - def test_partial_reset(self): - iterable = [str(n) for n in range(10)] - - s = mi.seekable(iterable) - self.assertEqual(mi.take(5, s), iterable[:5]) # Normal iteration - - s.seek(1) - self.assertEqual(list(s), iterable[1:]) # Get the rest of the iterable - - def test_forward(self): - iterable = [str(n) for n in range(10)] - - s = mi.seekable(iterable) - self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration - - s.seek(3) # Skip over index 2 - self.assertEqual(list(s), iterable[3:]) # Result is similar to slicing - - s.seek(0) # Back to 0 - self.assertEqual(list(s), iterable) # No difference in result - - def test_past_end(self): - iterable = [str(n) for n in range(10)] - - s = mi.seekable(iterable) - self.assertEqual(mi.take(1, s), iterable[:1]) # Normal iteration - - s.seek(20) - self.assertEqual(list(s), []) # Iterable is exhausted - - s.seek(0) # Back to 0 - self.assertEqual(list(s), iterable) # No difference in result - - def test_elements(self): - iterable = map(str, count()) - - s = mi.seekable(iterable) - mi.take(10, s) - - elements = s.elements() - self.assertEqual( - [elements[i] for i in range(10)], [str(n) for n in range(10)] - ) - self.assertEqual(len(elements), 10) - - mi.take(10, s) - self.assertEqual(list(elements), [str(n) for n in range(20)]) - - -class SequenceViewTests(TestCase): - def test_init(self): - view = mi.SequenceView((1, 2, 3)) - self.assertEqual(repr(view), "SequenceView((1, 2, 3))") - self.assertRaises(TypeError, lambda: mi.SequenceView({})) - - def test_update(self): - seq = [1, 2, 3] - view = mi.SequenceView(seq) - self.assertEqual(len(view), 3) - self.assertEqual(repr(view), "SequenceView([1, 2, 3])") - - seq.pop() - self.assertEqual(len(view), 2) - self.assertEqual(repr(view), "SequenceView([1, 2])") - - def test_indexing(self): - seq = ('a', 'b', 'c', 'd', 'e', 'f') - view = mi.SequenceView(seq) - for i in range(-len(seq), len(seq)): - self.assertEqual(view[i], seq[i]) - - def test_slicing(self): - seq = ('a', 'b', 'c', 'd', 'e', 'f') - view = mi.SequenceView(seq) - n = len(seq) - indexes = list(range(-n - 1, n + 1)) + [None] - steps = list(range(-n, n + 1)) - steps.remove(0) - for slice_args in product(indexes, indexes, steps): - i = slice(*slice_args) - self.assertEqual(view[i], seq[i]) - - def test_abc_methods(self): - # collections.Sequence should provide all of this functionality - seq = ('a', 'b', 'c', 'd', 'e', 'f', 'f') - view = mi.SequenceView(seq) - - # __contains__ - self.assertIn('b', view) - self.assertNotIn('g', view) - - # __iter__ - self.assertEqual(list(iter(view)), list(seq)) - - # __reversed__ - self.assertEqual(list(reversed(view)), list(reversed(seq))) - - # index - self.assertEqual(view.index('b'), 1) - - # count - self.assertEqual(seq.count('f'), 2) - - -class RunLengthTest(TestCase): - def test_encode(self): - iterable = (int(str(n)[0]) for n in count(800)) - actual = mi.take(4, mi.run_length.encode(iterable)) - expected = [(8, 100), (9, 100), (1, 1000), (2, 1000)] - self.assertEqual(actual, expected) - - def test_decode(self): - iterable = [('d', 4), ('c', 3), ('b', 2), ('a', 1)] - actual = ''.join(mi.run_length.decode(iterable)) - expected = 'ddddcccbba' - self.assertEqual(actual, expected) - - -class ExactlyNTests(TestCase): - """Tests for ``exactly_n()``""" - - def test_true(self): - """Iterable has ``n`` ``True`` elements""" - self.assertTrue(mi.exactly_n([True, False, True], 2)) - self.assertTrue(mi.exactly_n([1, 1, 1, 0], 3)) - self.assertTrue(mi.exactly_n([False, False], 0)) - self.assertTrue(mi.exactly_n(range(100), 10, lambda x: x < 10)) - - def test_false(self): - """Iterable does not have ``n`` ``True`` elements""" - self.assertFalse(mi.exactly_n([True, False, False], 2)) - self.assertFalse(mi.exactly_n([True, True, False], 1)) - self.assertFalse(mi.exactly_n([False], 1)) - self.assertFalse(mi.exactly_n([True], -1)) - self.assertFalse(mi.exactly_n(repeat(True), 100)) - - def test_empty(self): - """Return ``True`` if the iterable is empty and ``n`` is 0""" - self.assertTrue(mi.exactly_n([], 0)) - self.assertFalse(mi.exactly_n([], 1)) - - -class AlwaysReversibleTests(TestCase): - """Tests for ``always_reversible()``""" - - def test_regular_reversed(self): - self.assertEqual(list(reversed(range(10))), - list(mi.always_reversible(range(10)))) - self.assertEqual(list(reversed([1, 2, 3])), - list(mi.always_reversible([1, 2, 3]))) - self.assertEqual(reversed([1, 2, 3]).__class__, - mi.always_reversible([1, 2, 3]).__class__) - - def test_nonseq_reversed(self): - # Create a non-reversible generator from a sequence - with self.assertRaises(TypeError): - reversed(x for x in range(10)) - - self.assertEqual(list(reversed(range(10))), - list(mi.always_reversible(x for x in range(10)))) - self.assertEqual(list(reversed([1, 2, 3])), - list(mi.always_reversible(x for x in [1, 2, 3]))) - self.assertNotEqual(reversed((1, 2)).__class__, - mi.always_reversible(x for x in (1, 2)).__class__) - - -class CircularShiftsTests(TestCase): - def test_empty(self): - # empty iterable -> empty list - self.assertEqual(list(mi.circular_shifts([])), []) - - def test_simple_circular_shifts(self): - # test the a simple iterator case - self.assertEqual( - mi.circular_shifts(range(4)), - [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] - ) - - def test_duplicates(self): - # test non-distinct entries - self.assertEqual( - mi.circular_shifts([0, 1, 0, 1]), - [(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)] - ) - - -class MakeDecoratorTests(TestCase): - def test_basic(self): - slicer = mi.make_decorator(islice) - - @slicer(1, 10, 2) - def user_function(arg_1, arg_2, kwarg_1=None): - self.assertEqual(arg_1, 'arg_1') - self.assertEqual(arg_2, 'arg_2') - self.assertEqual(kwarg_1, 'kwarg_1') - return map(str, count()) - - it = user_function('arg_1', 'arg_2', kwarg_1='kwarg_1') - actual = list(it) - expected = ['1', '3', '5', '7', '9'] - self.assertEqual(actual, expected) - - def test_result_index(self): - def stringify(*args, **kwargs): - self.assertEqual(args[0], 'arg_0') - iterable = args[1] - self.assertEqual(args[2], 'arg_2') - self.assertEqual(kwargs['kwarg_1'], 'kwarg_1') - return map(str, iterable) - - stringifier = mi.make_decorator(stringify, result_index=1) - - @stringifier('arg_0', 'arg_2', kwarg_1='kwarg_1') - def user_function(n): - return count(n) - - it = user_function(1) - actual = mi.take(5, it) - expected = ['1', '2', '3', '4', '5'] - self.assertEqual(actual, expected) - - def test_wrap_class(self): - seeker = mi.make_decorator(mi.seekable) - - @seeker() - def user_function(n): - return map(str, range(n)) - - it = user_function(5) - self.assertEqual(list(it), ['0', '1', '2', '3', '4']) - - it.seek(0) - self.assertEqual(list(it), ['0', '1', '2', '3', '4']) - - -class MapReduceTests(TestCase): - def test_default(self): - iterable = (str(x) for x in range(5)) - keyfunc = lambda x: int(x) // 2 - actual = sorted(mi.map_reduce(iterable, keyfunc).items()) - expected = [(0, ['0', '1']), (1, ['2', '3']), (2, ['4'])] - self.assertEqual(actual, expected) - - def test_valuefunc(self): - iterable = (str(x) for x in range(5)) - keyfunc = lambda x: int(x) // 2 - valuefunc = int - actual = sorted(mi.map_reduce(iterable, keyfunc, valuefunc).items()) - expected = [(0, [0, 1]), (1, [2, 3]), (2, [4])] - self.assertEqual(actual, expected) - - def test_reducefunc(self): - iterable = (str(x) for x in range(5)) - keyfunc = lambda x: int(x) // 2 - valuefunc = int - reducefunc = lambda value_list: reduce(mul, value_list, 1) - actual = sorted( - mi.map_reduce(iterable, keyfunc, valuefunc, reducefunc).items() - ) - expected = [(0, 0), (1, 6), (2, 4)] - self.assertEqual(actual, expected) - - def test_ret(self): - d = mi.map_reduce([1, 0, 2, 0, 1, 0], bool) - self.assertEqual(d, {False: [0, 0, 0], True: [1, 2, 1]}) - self.assertRaises(KeyError, lambda: d[None].append(1)) - - -class RlocateTests(TestCase): - def test_default_pred(self): - iterable = [0, 1, 1, 0, 1, 0, 0] - for it in (iterable[:], iter(iterable)): - actual = list(mi.rlocate(it)) - expected = [4, 2, 1] - self.assertEqual(actual, expected) - - def test_no_matches(self): - iterable = [0, 0, 0] - for it in (iterable[:], iter(iterable)): - actual = list(mi.rlocate(it)) - expected = [] - self.assertEqual(actual, expected) - - def test_custom_pred(self): - iterable = ['0', 1, 1, '0', 1, '0', '0'] - pred = lambda x: x == '0' - for it in (iterable[:], iter(iterable)): - actual = list(mi.rlocate(it, pred)) - expected = [6, 5, 3, 0] - self.assertEqual(actual, expected) - - def test_efficient_reversal(self): - iterable = range(10 ** 10) # Is efficiently reversible - target = 10 ** 10 - 2 - pred = lambda x: x == target # Find-able from the right - actual = next(mi.rlocate(iterable, pred)) - self.assertEqual(actual, target) - - def test_window_size(self): - iterable = ['0', 1, 1, '0', 1, '0', '0'] - pred = lambda *args: args == ('0', 1) - for it in (iterable, iter(iterable)): - actual = list(mi.rlocate(it, pred, window_size=2)) - expected = [3, 0] - self.assertEqual(actual, expected) - - def test_window_size_large(self): - iterable = [1, 2, 3, 4] - pred = lambda a, b, c, d, e: True - for it in (iterable, iter(iterable)): - actual = list(mi.rlocate(iterable, pred, window_size=5)) - expected = [0] - self.assertEqual(actual, expected) - - def test_window_size_zero(self): - iterable = [1, 2, 3, 4] - pred = lambda: True - for it in (iterable, iter(iterable)): - with self.assertRaises(ValueError): - list(mi.locate(iterable, pred, window_size=0)) - - -class ReplaceTests(TestCase): - def test_basic(self): - iterable = range(10) - pred = lambda x: x % 2 == 0 - substitutes = [] - actual = list(mi.replace(iterable, pred, substitutes)) - expected = [1, 3, 5, 7, 9] - self.assertEqual(actual, expected) - - def test_count(self): - iterable = range(10) - pred = lambda x: x % 2 == 0 - substitutes = [] - actual = list(mi.replace(iterable, pred, substitutes, count=4)) - expected = [1, 3, 5, 7, 8, 9] - self.assertEqual(actual, expected) - - def test_window_size(self): - iterable = range(10) - pred = lambda *args: args == (0, 1, 2) - substitutes = [] - actual = list(mi.replace(iterable, pred, substitutes, window_size=3)) - expected = [3, 4, 5, 6, 7, 8, 9] - self.assertEqual(actual, expected) - - def test_window_size_end(self): - iterable = range(10) - pred = lambda *args: args == (7, 8, 9) - substitutes = [] - actual = list(mi.replace(iterable, pred, substitutes, window_size=3)) - expected = [0, 1, 2, 3, 4, 5, 6] - self.assertEqual(actual, expected) - - def test_window_size_count(self): - iterable = range(10) - pred = lambda *args: (args == (0, 1, 2)) or (args == (7, 8, 9)) - substitutes = [] - actual = list( - mi.replace(iterable, pred, substitutes, count=1, window_size=3) - ) - expected = [3, 4, 5, 6, 7, 8, 9] - self.assertEqual(actual, expected) - - def test_window_size_large(self): - iterable = range(4) - pred = lambda a, b, c, d, e: True - substitutes = [5, 6, 7] - actual = list(mi.replace(iterable, pred, substitutes, window_size=5)) - expected = [5, 6, 7] - self.assertEqual(actual, expected) - - def test_window_size_zero(self): - iterable = range(10) - pred = lambda *args: True - substitutes = [] - with self.assertRaises(ValueError): - list(mi.replace(iterable, pred, substitutes, window_size=0)) - - def test_iterable_substitutes(self): - iterable = range(5) - pred = lambda x: x % 2 == 0 - substitutes = iter('__') - actual = list(mi.replace(iterable, pred, substitutes)) - expected = ['_', '_', 1, '_', '_', 3, '_', '_'] - self.assertEqual(actual, expected) diff --git a/libs/win/more_itertools/tests/test_recipes.py b/libs/win/more_itertools/tests/test_recipes.py deleted file mode 100644 index 98981fe8..00000000 --- a/libs/win/more_itertools/tests/test_recipes.py +++ /dev/null @@ -1,616 +0,0 @@ -from doctest import DocTestSuite -from unittest import TestCase - -from itertools import combinations -from six.moves import range - -import more_itertools as mi - - -def load_tests(loader, tests, ignore): - # Add the doctests - tests.addTests(DocTestSuite('more_itertools.recipes')) - return tests - - -class AccumulateTests(TestCase): - """Tests for ``accumulate()``""" - - def test_empty(self): - """Test that an empty input returns an empty output""" - self.assertEqual(list(mi.accumulate([])), []) - - def test_default(self): - """Test accumulate with the default function (addition)""" - self.assertEqual(list(mi.accumulate([1, 2, 3])), [1, 3, 6]) - - def test_bogus_function(self): - """Test accumulate with an invalid function""" - with self.assertRaises(TypeError): - list(mi.accumulate([1, 2, 3], func=lambda x: x)) - - def test_custom_function(self): - """Test accumulate with a custom function""" - self.assertEqual( - list(mi.accumulate((1, 2, 3, 2, 1), func=max)), [1, 2, 3, 3, 3] - ) - - -class TakeTests(TestCase): - """Tests for ``take()``""" - - def test_simple_take(self): - """Test basic usage""" - t = mi.take(5, range(10)) - self.assertEqual(t, [0, 1, 2, 3, 4]) - - def test_null_take(self): - """Check the null case""" - t = mi.take(0, range(10)) - self.assertEqual(t, []) - - def test_negative_take(self): - """Make sure taking negative items results in a ValueError""" - self.assertRaises(ValueError, lambda: mi.take(-3, range(10))) - - def test_take_too_much(self): - """Taking more than an iterator has remaining should return what the - iterator has remaining. - - """ - t = mi.take(10, range(5)) - self.assertEqual(t, [0, 1, 2, 3, 4]) - - -class TabulateTests(TestCase): - """Tests for ``tabulate()``""" - - def test_simple_tabulate(self): - """Test the happy path""" - t = mi.tabulate(lambda x: x) - f = tuple([next(t) for _ in range(3)]) - self.assertEqual(f, (0, 1, 2)) - - def test_count(self): - """Ensure tabulate accepts specific count""" - t = mi.tabulate(lambda x: 2 * x, -1) - f = (next(t), next(t), next(t)) - self.assertEqual(f, (-2, 0, 2)) - - -class TailTests(TestCase): - """Tests for ``tail()``""" - - def test_greater(self): - """Length of iterable is greather than requested tail""" - self.assertEqual(list(mi.tail(3, 'ABCDEFG')), ['E', 'F', 'G']) - - def test_equal(self): - """Length of iterable is equal to the requested tail""" - self.assertEqual( - list(mi.tail(7, 'ABCDEFG')), ['A', 'B', 'C', 'D', 'E', 'F', 'G'] - ) - - def test_less(self): - """Length of iterable is less than requested tail""" - self.assertEqual( - list(mi.tail(8, 'ABCDEFG')), ['A', 'B', 'C', 'D', 'E', 'F', 'G'] - ) - - -class ConsumeTests(TestCase): - """Tests for ``consume()``""" - - def test_sanity(self): - """Test basic functionality""" - r = (x for x in range(10)) - mi.consume(r, 3) - self.assertEqual(3, next(r)) - - def test_null_consume(self): - """Check the null case""" - r = (x for x in range(10)) - mi.consume(r, 0) - self.assertEqual(0, next(r)) - - def test_negative_consume(self): - """Check that negative consumsion throws an error""" - r = (x for x in range(10)) - self.assertRaises(ValueError, lambda: mi.consume(r, -1)) - - def test_total_consume(self): - """Check that iterator is totally consumed by default""" - r = (x for x in range(10)) - mi.consume(r) - self.assertRaises(StopIteration, lambda: next(r)) - - -class NthTests(TestCase): - """Tests for ``nth()``""" - - def test_basic(self): - """Make sure the nth item is returned""" - l = range(10) - for i, v in enumerate(l): - self.assertEqual(mi.nth(l, i), v) - - def test_default(self): - """Ensure a default value is returned when nth item not found""" - l = range(3) - self.assertEqual(mi.nth(l, 100, "zebra"), "zebra") - - def test_negative_item_raises(self): - """Ensure asking for a negative item raises an exception""" - self.assertRaises(ValueError, lambda: mi.nth(range(10), -3)) - - -class AllEqualTests(TestCase): - """Tests for ``all_equal()``""" - - def test_true(self): - """Everything is equal""" - self.assertTrue(mi.all_equal('aaaaaa')) - self.assertTrue(mi.all_equal([0, 0, 0, 0])) - - def test_false(self): - """Not everything is equal""" - self.assertFalse(mi.all_equal('aaaaab')) - self.assertFalse(mi.all_equal([0, 0, 0, 1])) - - def test_tricky(self): - """Not everything is identical, but everything is equal""" - items = [1, complex(1, 0), 1.0] - self.assertTrue(mi.all_equal(items)) - - def test_empty(self): - """Return True if the iterable is empty""" - self.assertTrue(mi.all_equal('')) - self.assertTrue(mi.all_equal([])) - - def test_one(self): - """Return True if the iterable is singular""" - self.assertTrue(mi.all_equal('0')) - self.assertTrue(mi.all_equal([0])) - - -class QuantifyTests(TestCase): - """Tests for ``quantify()``""" - - def test_happy_path(self): - """Make sure True count is returned""" - q = [True, False, True] - self.assertEqual(mi.quantify(q), 2) - - def test_custom_predicate(self): - """Ensure non-default predicates return as expected""" - q = range(10) - self.assertEqual(mi.quantify(q, lambda x: x % 2 == 0), 5) - - -class PadnoneTests(TestCase): - """Tests for ``padnone()``""" - - def test_happy_path(self): - """wrapper iterator should return None indefinitely""" - r = range(2) - p = mi.padnone(r) - self.assertEqual([0, 1, None, None], [next(p) for _ in range(4)]) - - -class NcyclesTests(TestCase): - """Tests for ``nyclces()``""" - - def test_happy_path(self): - """cycle a sequence three times""" - r = ["a", "b", "c"] - n = mi.ncycles(r, 3) - self.assertEqual( - ["a", "b", "c", "a", "b", "c", "a", "b", "c"], - list(n) - ) - - def test_null_case(self): - """asking for 0 cycles should return an empty iterator""" - n = mi.ncycles(range(100), 0) - self.assertRaises(StopIteration, lambda: next(n)) - - def test_pathalogical_case(self): - """asking for negative cycles should return an empty iterator""" - n = mi.ncycles(range(100), -10) - self.assertRaises(StopIteration, lambda: next(n)) - - -class DotproductTests(TestCase): - """Tests for ``dotproduct()``'""" - - def test_happy_path(self): - """simple dotproduct example""" - self.assertEqual(400, mi.dotproduct([10, 10], [20, 20])) - - -class FlattenTests(TestCase): - """Tests for ``flatten()``""" - - def test_basic_usage(self): - """ensure list of lists is flattened one level""" - f = [[0, 1, 2], [3, 4, 5]] - self.assertEqual(list(range(6)), list(mi.flatten(f))) - - def test_single_level(self): - """ensure list of lists is flattened only one level""" - f = [[0, [1, 2]], [[3, 4], 5]] - self.assertEqual([0, [1, 2], [3, 4], 5], list(mi.flatten(f))) - - -class RepeatfuncTests(TestCase): - """Tests for ``repeatfunc()``""" - - def test_simple_repeat(self): - """test simple repeated functions""" - r = mi.repeatfunc(lambda: 5) - self.assertEqual([5, 5, 5, 5, 5], [next(r) for _ in range(5)]) - - def test_finite_repeat(self): - """ensure limited repeat when times is provided""" - r = mi.repeatfunc(lambda: 5, times=5) - self.assertEqual([5, 5, 5, 5, 5], list(r)) - - def test_added_arguments(self): - """ensure arguments are applied to the function""" - r = mi.repeatfunc(lambda x: x, 2, 3) - self.assertEqual([3, 3], list(r)) - - def test_null_times(self): - """repeat 0 should return an empty iterator""" - r = mi.repeatfunc(range, 0, 3) - self.assertRaises(StopIteration, lambda: next(r)) - - -class PairwiseTests(TestCase): - """Tests for ``pairwise()``""" - - def test_base_case(self): - """ensure an iterable will return pairwise""" - p = mi.pairwise([1, 2, 3]) - self.assertEqual([(1, 2), (2, 3)], list(p)) - - def test_short_case(self): - """ensure an empty iterator if there's not enough values to pair""" - p = mi.pairwise("a") - self.assertRaises(StopIteration, lambda: next(p)) - - -class GrouperTests(TestCase): - """Tests for ``grouper()``""" - - def test_even(self): - """Test when group size divides evenly into the length of - the iterable. - - """ - self.assertEqual( - list(mi.grouper(3, 'ABCDEF')), [('A', 'B', 'C'), ('D', 'E', 'F')] - ) - - def test_odd(self): - """Test when group size does not divide evenly into the length of the - iterable. - - """ - self.assertEqual( - list(mi.grouper(3, 'ABCDE')), [('A', 'B', 'C'), ('D', 'E', None)] - ) - - def test_fill_value(self): - """Test that the fill value is used to pad the final group""" - self.assertEqual( - list(mi.grouper(3, 'ABCDE', 'x')), - [('A', 'B', 'C'), ('D', 'E', 'x')] - ) - - -class RoundrobinTests(TestCase): - """Tests for ``roundrobin()``""" - - def test_even_groups(self): - """Ensure ordered output from evenly populated iterables""" - self.assertEqual( - list(mi.roundrobin('ABC', [1, 2, 3], range(3))), - ['A', 1, 0, 'B', 2, 1, 'C', 3, 2] - ) - - def test_uneven_groups(self): - """Ensure ordered output from unevenly populated iterables""" - self.assertEqual( - list(mi.roundrobin('ABCD', [1, 2], range(0))), - ['A', 1, 'B', 2, 'C', 'D'] - ) - - -class PartitionTests(TestCase): - """Tests for ``partition()``""" - - def test_bool(self): - """Test when pred() returns a boolean""" - lesser, greater = mi.partition(lambda x: x > 5, range(10)) - self.assertEqual(list(lesser), [0, 1, 2, 3, 4, 5]) - self.assertEqual(list(greater), [6, 7, 8, 9]) - - def test_arbitrary(self): - """Test when pred() returns an integer""" - divisibles, remainders = mi.partition(lambda x: x % 3, range(10)) - self.assertEqual(list(divisibles), [0, 3, 6, 9]) - self.assertEqual(list(remainders), [1, 2, 4, 5, 7, 8]) - - -class PowersetTests(TestCase): - """Tests for ``powerset()``""" - - def test_combinatorics(self): - """Ensure a proper enumeration""" - p = mi.powerset([1, 2, 3]) - self.assertEqual( - list(p), - [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] - ) - - -class UniqueEverseenTests(TestCase): - """Tests for ``unique_everseen()``""" - - def test_everseen(self): - """ensure duplicate elements are ignored""" - u = mi.unique_everseen('AAAABBBBCCDAABBB') - self.assertEqual( - ['A', 'B', 'C', 'D'], - list(u) - ) - - def test_custom_key(self): - """ensure the custom key comparison works""" - u = mi.unique_everseen('aAbACCc', key=str.lower) - self.assertEqual(list('abC'), list(u)) - - def test_unhashable(self): - """ensure things work for unhashable items""" - iterable = ['a', [1, 2, 3], [1, 2, 3], 'a'] - u = mi.unique_everseen(iterable) - self.assertEqual(list(u), ['a', [1, 2, 3]]) - - def test_unhashable_key(self): - """ensure things work for unhashable items with a custom key""" - iterable = ['a', [1, 2, 3], [1, 2, 3], 'a'] - u = mi.unique_everseen(iterable, key=lambda x: x) - self.assertEqual(list(u), ['a', [1, 2, 3]]) - - -class UniqueJustseenTests(TestCase): - """Tests for ``unique_justseen()``""" - - def test_justseen(self): - """ensure only last item is remembered""" - u = mi.unique_justseen('AAAABBBCCDABB') - self.assertEqual(list('ABCDAB'), list(u)) - - def test_custom_key(self): - """ensure the custom key comparison works""" - u = mi.unique_justseen('AABCcAD', str.lower) - self.assertEqual(list('ABCAD'), list(u)) - - -class IterExceptTests(TestCase): - """Tests for ``iter_except()``""" - - def test_exact_exception(self): - """ensure the exact specified exception is caught""" - l = [1, 2, 3] - i = mi.iter_except(l.pop, IndexError) - self.assertEqual(list(i), [3, 2, 1]) - - def test_generic_exception(self): - """ensure the generic exception can be caught""" - l = [1, 2] - i = mi.iter_except(l.pop, Exception) - self.assertEqual(list(i), [2, 1]) - - def test_uncaught_exception_is_raised(self): - """ensure a non-specified exception is raised""" - l = [1, 2, 3] - i = mi.iter_except(l.pop, KeyError) - self.assertRaises(IndexError, lambda: list(i)) - - def test_first(self): - """ensure first is run before the function""" - l = [1, 2, 3] - f = lambda: 25 - i = mi.iter_except(l.pop, IndexError, f) - self.assertEqual(list(i), [25, 3, 2, 1]) - - -class FirstTrueTests(TestCase): - """Tests for ``first_true()``""" - - def test_something_true(self): - """Test with no keywords""" - self.assertEqual(mi.first_true(range(10)), 1) - - def test_nothing_true(self): - """Test default return value.""" - self.assertEqual(mi.first_true([0, 0, 0]), False) - - def test_default(self): - """Test with a default keyword""" - self.assertEqual(mi.first_true([0, 0, 0], default='!'), '!') - - def test_pred(self): - """Test with a custom predicate""" - self.assertEqual( - mi.first_true([2, 4, 6], pred=lambda x: x % 3 == 0), 6 - ) - - -class RandomProductTests(TestCase): - """Tests for ``random_product()`` - - Since random.choice() has different results with the same seed across - python versions 2.x and 3.x, these tests use highly probably events to - create predictable outcomes across platforms. - """ - - def test_simple_lists(self): - """Ensure that one item is chosen from each list in each pair. - Also ensure that each item from each list eventually appears in - the chosen combinations. - - Odds are roughly 1 in 7.1 * 10e16 that one item from either list will - not be chosen after 100 samplings of one item from each list. Just to - be safe, better use a known random seed, too. - - """ - nums = [1, 2, 3] - lets = ['a', 'b', 'c'] - n, m = zip(*[mi.random_product(nums, lets) for _ in range(100)]) - n, m = set(n), set(m) - self.assertEqual(n, set(nums)) - self.assertEqual(m, set(lets)) - self.assertEqual(len(n), len(nums)) - self.assertEqual(len(m), len(lets)) - - def test_list_with_repeat(self): - """ensure multiple items are chosen, and that they appear to be chosen - from one list then the next, in proper order. - - """ - nums = [1, 2, 3] - lets = ['a', 'b', 'c'] - r = list(mi.random_product(nums, lets, repeat=100)) - self.assertEqual(2 * 100, len(r)) - n, m = set(r[::2]), set(r[1::2]) - self.assertEqual(n, set(nums)) - self.assertEqual(m, set(lets)) - self.assertEqual(len(n), len(nums)) - self.assertEqual(len(m), len(lets)) - - -class RandomPermutationTests(TestCase): - """Tests for ``random_permutation()``""" - - def test_full_permutation(self): - """ensure every item from the iterable is returned in a new ordering - - 15 elements have a 1 in 1.3 * 10e12 of appearing in sorted order, so - we fix a seed value just to be sure. - - """ - i = range(15) - r = mi.random_permutation(i) - self.assertEqual(set(i), set(r)) - if i == r: - raise AssertionError("Values were not permuted") - - def test_partial_permutation(self): - """ensure all returned items are from the iterable, that the returned - permutation is of the desired length, and that all items eventually - get returned. - - Sampling 100 permutations of length 5 from a set of 15 leaves a - (2/3)^100 chance that an item will not be chosen. Multiplied by 15 - items, there is a 1 in 2.6e16 chance that at least 1 item will not - show up in the resulting output. Using a random seed will fix that. - - """ - items = range(15) - item_set = set(items) - all_items = set() - for _ in range(100): - permutation = mi.random_permutation(items, 5) - self.assertEqual(len(permutation), 5) - permutation_set = set(permutation) - self.assertLessEqual(permutation_set, item_set) - all_items |= permutation_set - self.assertEqual(all_items, item_set) - - -class RandomCombinationTests(TestCase): - """Tests for ``random_combination()``""" - - def test_psuedorandomness(self): - """ensure different subsets of the iterable get returned over many - samplings of random combinations""" - items = range(15) - all_items = set() - for _ in range(50): - combination = mi.random_combination(items, 5) - all_items |= set(combination) - self.assertEqual(all_items, set(items)) - - def test_no_replacement(self): - """ensure that elements are sampled without replacement""" - items = range(15) - for _ in range(50): - combination = mi.random_combination(items, len(items)) - self.assertEqual(len(combination), len(set(combination))) - self.assertRaises( - ValueError, lambda: mi.random_combination(items, len(items) + 1) - ) - - -class RandomCombinationWithReplacementTests(TestCase): - """Tests for ``random_combination_with_replacement()``""" - - def test_replacement(self): - """ensure that elements are sampled with replacement""" - items = range(5) - combo = mi.random_combination_with_replacement(items, len(items) * 2) - self.assertEqual(2 * len(items), len(combo)) - if len(set(combo)) == len(combo): - raise AssertionError("Combination contained no duplicates") - - def test_pseudorandomness(self): - """ensure different subsets of the iterable get returned over many - samplings of random combinations""" - items = range(15) - all_items = set() - for _ in range(50): - combination = mi.random_combination_with_replacement(items, 5) - all_items |= set(combination) - self.assertEqual(all_items, set(items)) - - -class NthCombinationTests(TestCase): - def test_basic(self): - iterable = 'abcdefg' - r = 4 - for index, expected in enumerate(combinations(iterable, r)): - actual = mi.nth_combination(iterable, r, index) - self.assertEqual(actual, expected) - - def test_long(self): - actual = mi.nth_combination(range(180), 4, 2000000) - expected = (2, 12, 35, 126) - self.assertEqual(actual, expected) - - def test_invalid_r(self): - for r in (-1, 3): - with self.assertRaises(ValueError): - mi.nth_combination([], r, 0) - - def test_invalid_index(self): - with self.assertRaises(IndexError): - mi.nth_combination('abcdefg', 3, -36) - - -class PrependTests(TestCase): - def test_basic(self): - value = 'a' - iterator = iter('bcdefg') - actual = list(mi.prepend(value, iterator)) - expected = list('abcdefg') - self.assertEqual(actual, expected) - - def test_multiple(self): - value = 'ab' - iterator = iter('cdefg') - actual = tuple(mi.prepend(value, iterator)) - expected = ('ab',) + tuple('cdefg') - self.assertEqual(actual, expected) diff --git a/libs/win/path.py b/libs/win/path/__init__.py similarity index 53% rename from libs/win/path.py rename to libs/win/path/__init__.py index 69ac5c13..f16857f8 100644 --- a/libs/win/path.py +++ b/libs/win/path/__init__.py @@ -1,7 +1,8 @@ """ -path.py - An object representing a path to a file or directory. +Path Pie -https://github.com/jaraco/path.py +Implements ``path.Path`` - An object representing a +path to a file or directory. Example:: @@ -21,8 +22,6 @@ Example:: foo_txt = Path("bar") / "foo.txt" """ -from __future__ import unicode_literals - import sys import warnings import os @@ -33,183 +32,89 @@ import hashlib import errno import tempfile import functools -import operator import re import contextlib import io import importlib import itertools -import platform -import ntpath -try: +with contextlib.suppress(ImportError): import win32security -except ImportError: - pass -try: +with contextlib.suppress(ImportError): import pwd -except ImportError: - pass -try: +with contextlib.suppress(ImportError): import grp -except ImportError: - pass -############################################################################## -# Python 2/3 support -PY3 = sys.version_info >= (3,) -PY2 = not PY3 - -string_types = str, -text_type = str -getcwdu = os.getcwd +from . import matchers +from . import masks +from . import classes +from .py37compat import best_realpath, lru_cache -if PY2: - import __builtin__ - string_types = __builtin__.basestring, - text_type = __builtin__.unicode - getcwdu = os.getcwdu - map = itertools.imap - filter = itertools.ifilter - FileNotFoundError = OSError - itertools.filterfalse = itertools.ifilterfalse - - -@contextlib.contextmanager -def io_error_compat(): - try: - yield - except IOError as io_err: - # On Python 2, io.open raises IOError; transform to OSError for - # future compatibility. - os_err = OSError(*io_err.args) - os_err.filename = getattr(io_err, 'filename', None) - raise os_err - -############################################################################## - - -__all__ = ['Path', 'TempDir', 'CaseInsensitivePattern'] +__all__ = ['Path', 'TempDir'] LINESEPS = ['\r\n', '\r', '\n'] U_LINESEPS = LINESEPS + ['\u0085', '\u2028', '\u2029'] -NEWLINE = re.compile('|'.join(LINESEPS)) +B_NEWLINE = re.compile('|'.join(LINESEPS).encode()) U_NEWLINE = re.compile('|'.join(U_LINESEPS)) -NL_END = re.compile(r'(?:{0})$'.format(NEWLINE.pattern)) -U_NL_END = re.compile(r'(?:{0})$'.format(U_NEWLINE.pattern)) +B_NL_END = re.compile(B_NEWLINE.pattern + b'$') +U_NL_END = re.compile(U_NEWLINE.pattern + '$') - -try: - import importlib_metadata - __version__ = importlib_metadata.version('path.py') -except Exception: - __version__ = 'unknown' +_default_linesep = object() class TreeWalkWarning(Warning): pass -# from jaraco.functools -def compose(*funcs): - compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs)) # noqa - return functools.reduce(compose_two, funcs) - - -def simple_cache(func): +class Traversal: """ - Save results for the :meth:'path.using_module' classmethod. - When Python 3.2 is available, use functools.lru_cache instead. + Wrap a walk result to customize the traversal. + + `follow` is a function that takes an item and returns + True if that item should be followed and False otherwise. + + For example, to avoid traversing into directories that + begin with `.`: + + >>> traverse = Traversal(lambda dir: not dir.startswith('.')) + >>> items = list(traverse(Path('.').walk())) + + Directories beginning with `.` will appear in the results, but + their children will not. + + >>> dot_dir = next(item for item in items if item.isdir() and item.startswith('.')) + >>> any(item.parent == dot_dir for item in items) + False """ - saved_results = {} - def wrapper(cls, module): - if module in saved_results: - return saved_results[module] - saved_results[module] = func(cls, module) - return saved_results[module] - return wrapper + def __init__(self, follow): + self.follow = follow - -class ClassProperty(property): - def __get__(self, cls, owner): - return self.fget.__get__(None, owner)() - - -class multimethod(object): - """ - Acts like a classmethod when invoked from the class and like an - instancemethod when invoked from the instance. - """ - def __init__(self, func): - self.func = func - - def __get__(self, instance, owner): - return ( - functools.partial(self.func, owner) if instance is None - else functools.partial(self.func, owner, instance) - ) - - -class matchers(object): - # TODO: make this class a module - - @staticmethod - def load(param): - """ - If the supplied parameter is a string, assum it's a simple - pattern. - """ - return ( - matchers.Pattern(param) if isinstance(param, string_types) - else param if param is not None - else matchers.Null() - ) - - class Base(object): - pass - - class Null(Base): - def __call__(self, path): - return True - - class Pattern(Base): - def __init__(self, pattern): - self.pattern = pattern - - def get_pattern(self, normcase): + def __call__(self, walker): + traverse = None + while True: try: - return self._pattern - except AttributeError: - pass - self._pattern = normcase(self.pattern) - return self._pattern + item = walker.send(traverse) + except StopIteration: + return + yield item - def __call__(self, path): - normcase = getattr(self, 'normcase', path.module.normcase) - pattern = self.get_pattern(normcase) - return fnmatch.fnmatchcase(normcase(path.name), pattern) - - class CaseInsensitive(Pattern): - """ - A Pattern with a ``'normcase'`` property, suitable for passing to - :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`, - :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive. - - For example, to get all files ending in .py, .Py, .pY, or .PY in the - current directory:: - - from path import Path, matchers - Path('.').files(matchers.CaseInsensitive('*.py')) - """ - normcase = staticmethod(ntpath.normcase) + traverse = functools.partial(self.follow, item) -class Path(text_type): +def _strip_newlines(lines): + r""" + >>> list(_strip_newlines(['Hello World\r\n', 'foo'])) + ['Hello World', 'foo'] + """ + return (U_NL_END.sub('', line) for line in lines) + + +class Path(str): """ Represents a filesystem path. @@ -234,18 +139,18 @@ class Path(text_type): def __init__(self, other=''): if other is None: raise TypeError("Invalid initial value for path: None") + with contextlib.suppress(AttributeError): + self._validate() @classmethod - @simple_cache + @lru_cache def using_module(cls, module): subclass_name = cls.__name__ + '_' + module.__name__ - if PY2: - subclass_name = str(subclass_name) bases = (cls,) ns = {'module': module} return type(subclass_name, bases, ns) - @ClassProperty + @classes.ClassProperty @classmethod def _next_class(cls): """ @@ -260,19 +165,14 @@ class Path(text_type): # Adding a Path and a string yields a Path. def __add__(self, more): - try: - return self._next_class(super(Path, self).__add__(more)) - except TypeError: # Python bug - return NotImplemented + return self._next_class(super(Path, self).__add__(more)) def __radd__(self, other): - if not isinstance(other, string_types): - return NotImplemented return self._next_class(other.__add__(self)) # The / operator joins Paths. def __div__(self, rel): - """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) + """fp.__div__(rel) == fp / rel == fp.joinpath(rel) Join two path components, adding a separator character if needed. @@ -286,7 +186,7 @@ class Path(text_type): # The / operator joins Paths the other way around def __rdiv__(self, rel): - """ fp.__rdiv__(rel) == rel / fp + """fp.__rdiv__(rel) == rel / fp Join two path components, adding a separator character if needed. @@ -306,54 +206,52 @@ class Path(text_type): def __exit__(self, *_): os.chdir(self._old_dir) - def __fspath__(self): - return self - @classmethod def getcwd(cls): - """ Return the current working directory as a path object. + """Return the current working directory as a path object. - .. seealso:: :func:`os.getcwdu` + .. seealso:: :func:`os.getcwd` """ - return cls(getcwdu()) + return cls(os.getcwd()) # # --- Operations on Path strings. def abspath(self): - """ .. seealso:: :func:`os.path.abspath` """ + """.. seealso:: :func:`os.path.abspath`""" return self._next_class(self.module.abspath(self)) def normcase(self): - """ .. seealso:: :func:`os.path.normcase` """ + """.. seealso:: :func:`os.path.normcase`""" return self._next_class(self.module.normcase(self)) def normpath(self): - """ .. seealso:: :func:`os.path.normpath` """ + """.. seealso:: :func:`os.path.normpath`""" return self._next_class(self.module.normpath(self)) def realpath(self): - """ .. seealso:: :func:`os.path.realpath` """ - return self._next_class(self.module.realpath(self)) + """.. seealso:: :func:`os.path.realpath`""" + realpath = best_realpath(self.module) + return self._next_class(realpath(self)) def expanduser(self): - """ .. seealso:: :func:`os.path.expanduser` """ + """.. seealso:: :func:`os.path.expanduser`""" return self._next_class(self.module.expanduser(self)) def expandvars(self): - """ .. seealso:: :func:`os.path.expandvars` """ + """.. seealso:: :func:`os.path.expandvars`""" return self._next_class(self.module.expandvars(self)) def dirname(self): - """ .. seealso:: :attr:`parent`, :func:`os.path.dirname` """ + """.. seealso:: :attr:`parent`, :func:`os.path.dirname`""" return self._next_class(self.module.dirname(self)) def basename(self): - """ .. seealso:: :attr:`name`, :func:`os.path.basename` """ + """.. seealso:: :attr:`name`, :func:`os.path.basename`""" return self._next_class(self.module.basename(self)) def expand(self): - """ Clean up a filename by calling :meth:`expandvars()`, + """Clean up a filename by calling :meth:`expandvars()`, :meth:`expanduser()`, and :meth:`normpath()` on it. This is commonly everything needed to clean up a filename @@ -363,7 +261,7 @@ class Path(text_type): @property def stem(self): - """ The same as :meth:`name`, but with one file extension stripped off. + """The same as :meth:`name`, but with one file extension stripped off. >>> Path('/home/guido/python.tar.gz').stem 'python.tar' @@ -371,19 +269,14 @@ class Path(text_type): base, ext = self.module.splitext(self.name) return base - @property - def namebase(self): - warnings.warn("Use .stem instead of .namebase", DeprecationWarning) - return self.stem - @property def ext(self): - """ The file extension, for example ``'.py'``. """ + """The file extension, for example ``'.py'``.""" f, ext = self.module.splitext(self) return ext def with_suffix(self, suffix): - """ Return a new path with the file suffix changed (or added, if none) + """Return a new path with the file suffix changed (or added, if none) >>> Path('/home/guido/python.tar.gz').with_suffix(".foo") Path('/home/guido/python.tar.foo') @@ -403,7 +296,7 @@ class Path(text_type): @property def drive(self): - """ The drive specifier, for example ``'C:'``. + """The drive specifier, for example ``'C:'``. This is always empty on systems that don't use drive specifiers. """ @@ -411,7 +304,9 @@ class Path(text_type): return self._next_class(drive) parent = property( - dirname, None, None, + dirname, + None, + None, """ This path's parent directory, as a new Path object. For example, @@ -419,20 +314,24 @@ class Path(text_type): Path('/usr/local/lib')`` .. seealso:: :meth:`dirname`, :func:`os.path.dirname` - """) + """, + ) name = property( - basename, None, None, + basename, + None, + None, """ The name of this file or directory without the full path. For example, ``Path('/usr/local/lib/libpython.so').name == 'libpython.so'`` .. seealso:: :meth:`basename`, :func:`os.path.basename` - """) + """, + ) def splitpath(self): - """ p.splitpath() -> Return ``(p.parent, p.name)``. + """Return two-tuple of ``.parent``, ``.name``. .. seealso:: :attr:`parent`, :attr:`name`, :func:`os.path.split` """ @@ -440,7 +339,7 @@ class Path(text_type): return self._next_class(parent), child def splitdrive(self): - """ p.splitdrive() -> Return ``(p.drive, )``. + """Return two-tuple of ``.drive`` and rest without drive. Split the drive specifier from this path. If there is no drive specifier, :samp:`{p.drive}` is empty, so the return value @@ -449,10 +348,10 @@ class Path(text_type): .. seealso:: :func:`os.path.splitdrive` """ drive, rel = self.module.splitdrive(self) - return self._next_class(drive), rel + return self._next_class(drive), self._next_class(rel) def splitext(self): - """ p.splitext() -> Return ``(p.stripext(), p.ext)``. + """Return two-tuple of ``.stripext()`` and ``.ext``. Split the filename extension from this path and return the two parts. Either part may be empty. @@ -467,28 +366,14 @@ class Path(text_type): return self._next_class(filename), ext def stripext(self): - """ p.stripext() -> Remove one file extension from the path. + """Remove one file extension from the path. For example, ``Path('/home/guido/python.tar.gz').stripext()`` returns ``Path('/home/guido/python.tar')``. """ return self.splitext()[0] - def splitunc(self): - """ .. seealso:: :func:`os.path.splitunc` """ - unc, rest = self.module.splitunc(self) - return self._next_class(unc), rest - - @property - def uncshare(self): - """ - The UNC mount point for this path. - This is empty for paths on local drives. - """ - unc, r = self.module.splitunc(self) - return self._next_class(unc) - - @multimethod + @classes.multimethod def joinpath(cls, first, *others): """ Join first to zero or more :class:`Path` components, @@ -498,41 +383,52 @@ class Path(text_type): .. seealso:: :func:`os.path.join` """ - if not isinstance(first, cls): - first = cls(first) - return first._next_class(first.module.join(first, *others)) + return cls._next_class(cls.module.join(first, *others)) def splitall(self): - r""" Return a list of the path components in this path. + r"""Return a list of the path components in this path. The first item in the list will be a Path. Its value will be either :data:`os.curdir`, :data:`os.pardir`, empty, or the root directory of this path (for example, ``'/'`` or ``'C:\\'``). The other items in the list will be strings. - ``path.Path.joinpath(*result)`` will yield the original path. + ``Path.joinpath(*result)`` will yield the original path. + + >>> Path('/foo/bar/baz').splitall() + [Path('/'), 'foo', 'bar', 'baz'] """ - parts = [] + return list(self._parts()) + + def parts(self): + """ + >>> Path('/foo/bar/baz').parts() + (Path('/'), 'foo', 'bar', 'baz') + """ + return tuple(self._parts()) + + def _parts(self): + return reversed(tuple(self._parts_iter())) + + def _parts_iter(self): loc = self while loc != os.curdir and loc != os.pardir: prev = loc loc, child = prev.splitpath() if loc == prev: break - parts.append(child) - parts.append(loc) - parts.reverse() - return parts + yield child + yield loc def relpath(self, start='.'): - """ Return this path as a relative path, + """Return this path as a relative path, based from `start`, which defaults to the current working directory. """ cwd = self._next_class(start) return cwd.relpathto(self) def relpathto(self, dest): - """ Return a relative path from `self` to `dest`. + """Return a relative path from `self` to `dest`. If there is no relative path from `self` to `dest`, for example if they reside on different drives in Windows, then this returns @@ -572,7 +468,7 @@ class Path(text_type): # --- Listing, searching, walking, and matching def listdir(self, match=None): - """ D.listdir() -> List of items in this directory. + """List of items in this directory. Use :meth:`files` or :meth:`dirs` instead if you want a listing of just files or just subdirectories. @@ -585,12 +481,10 @@ class Path(text_type): .. seealso:: :meth:`files`, :meth:`dirs` """ match = matchers.load(match) - return list(filter(match, ( - self / child for child in os.listdir(self) - ))) + return list(filter(match, (self / child for child in os.listdir(self)))) def dirs(self, *args, **kwargs): - """ D.dirs() -> List of this directory's subdirectories. + """List of this directory's subdirectories. The elements of the list are Path objects. This does not walk recursively into subdirectories @@ -601,7 +495,7 @@ class Path(text_type): return [p for p in self.listdir(*args, **kwargs) if p.isdir()] def files(self, *args, **kwargs): - """ D.files() -> List of the files in this directory. + """List of the files in self. The elements of the list are Path objects. This does not walk into subdirectories (see :meth:`walkfiles`). @@ -612,7 +506,7 @@ class Path(text_type): return [p for p in self.listdir(*args, **kwargs) if p.isfile()] def walk(self, match=None, errors='strict'): - """ D.walk() -> iterator over files and subdirs, recursively. + """Iterator over files and subdirs, recursively. The iterator yields Path objects naming each child item of this directory and its descendants. This requires that @@ -627,75 +521,49 @@ class Path(text_type): reports the error via :func:`warnings.warn()`), and ``'ignore'``. `errors` may also be an arbitrary callable taking a msg parameter. """ - class Handlers: - def strict(msg): - raise - - def warn(msg): - warnings.warn(msg, TreeWalkWarning) - - def ignore(msg): - pass - - if not callable(errors) and errors not in vars(Handlers): - raise ValueError("invalid errors parameter") - errors = vars(Handlers).get(errors, errors) + errors = Handlers._resolve(errors) match = matchers.load(match) try: childList = self.listdir() - except Exception: - exc = sys.exc_info()[1] - tmpl = "Unable to list directory '%(self)s': %(exc)s" - msg = tmpl % locals() - errors(msg) + except Exception as exc: + errors(f"Unable to list directory '{self}': {exc}") return for child in childList: + traverse = None if match(child): - yield child + traverse = yield child + traverse = traverse or child.isdir try: - isdir = child.isdir() - except Exception: - exc = sys.exc_info()[1] - tmpl = "Unable to access '%(child)s': %(exc)s" - msg = tmpl % locals() - errors(msg) - isdir = False + do_traverse = traverse() + except Exception as exc: + errors(f"Unable to access '{child}': {exc}") + continue - if isdir: + if do_traverse: for item in child.walk(errors=errors, match=match): yield item def walkdirs(self, *args, **kwargs): - """ D.walkdirs() -> iterator over subdirs, recursively. - """ - return ( - item - for item in self.walk(*args, **kwargs) - if item.isdir() - ) + """Iterator over subdirs, recursively.""" + return (item for item in self.walk(*args, **kwargs) if item.isdir()) def walkfiles(self, *args, **kwargs): - """ D.walkfiles() -> iterator over files in D, recursively. - """ - return ( - item - for item in self.walk(*args, **kwargs) - if item.isfile() - ) + """Iterator over files, recursively.""" + return (item for item in self.walk(*args, **kwargs) if item.isfile()) def fnmatch(self, pattern, normcase=None): - """ Return ``True`` if `self.name` matches the given `pattern`. + """Return ``True`` if `self.name` matches the given `pattern`. `pattern` - A filename pattern with wildcards, for example ``'*.py'``. If the pattern contains a `normcase` attribute, it is applied to the name and path prior to comparison. `normcase` - (optional) A function used to normalize the pattern and - filename before matching. Defaults to :meth:`self.module`, which - defaults to :meth:`os.path.normcase`. + filename before matching. Defaults to normcase from + ``self.module``, :func:`os.path.normcase`. .. seealso:: :func:`fnmatch.fnmatch` """ @@ -706,7 +574,7 @@ class Path(text_type): return fnmatch.fnmatchcase(name, pattern) def glob(self, pattern): - """ Return a list of Path objects that match the pattern. + """Return a list of Path objects that match the pattern. `pattern` - a path relative to this directory, with wildcards. @@ -723,7 +591,7 @@ class Path(text_type): return [cls(s) for s in glob.glob(self / pattern)] def iglob(self, pattern): - """ Return an iterator of Path objects that match the pattern. + """Return an iterator of Path objects that match the pattern. `pattern` - a path relative to this directory, with wildcards. @@ -744,64 +612,72 @@ class Path(text_type): # --- Reading or writing an entire file at once. def open(self, *args, **kwargs): - """ Open this file and return a corresponding :class:`file` object. + """Open this file and return a corresponding file object. Keyword arguments work as in :func:`io.open`. If the file cannot be - opened, an :class:`~exceptions.OSError` is raised. + opened, an :class:`OSError` is raised. """ - with io_error_compat(): - return io.open(self, *args, **kwargs) + return io.open(self, *args, **kwargs) def bytes(self): - """ Open this file, read all bytes, return them as a string. """ + """Open this file, read all bytes, return them as a string.""" with self.open('rb') as f: return f.read() def chunks(self, size, *args, **kwargs): - """ Returns a generator yielding chunks of the file, so it can - be read piece by piece with a simple for loop. + """Returns a generator yielding chunks of the file, so it can + be read piece by piece with a simple for loop. - Any argument you pass after `size` will be passed to :meth:`open`. + Any argument you pass after `size` will be passed to :meth:`open`. - :example: + :example: - >>> hash = hashlib.md5() - >>> for chunk in Path("path.py").chunks(8192, mode='rb'): - ... hash.update(chunk) + >>> hash = hashlib.md5() + >>> for chunk in Path("CHANGES.rst").chunks(8192, mode='rb'): + ... hash.update(chunk) - This will read the file by chunks of 8192 bytes. + This will read the file by chunks of 8192 bytes. """ with self.open(*args, **kwargs) as f: for chunk in iter(lambda: f.read(size) or None, None): yield chunk def write_bytes(self, bytes, append=False): - """ Open this file and write the given bytes to it. + """Open this file and write the given bytes to it. Default behavior is to overwrite any existing file. Call ``p.write_bytes(bytes, append=True)`` to append instead. """ - if append: - mode = 'ab' - else: - mode = 'wb' - with self.open(mode) as f: + with self.open('ab' if append else 'wb') as f: f.write(bytes) - def text(self, encoding=None, errors='strict'): - r""" Open this file, read it in, return the content as a string. + def read_text(self, encoding=None, errors=None): + r"""Open this file, read it in, return the content as a string. - All newline sequences are converted to ``'\n'``. Keyword arguments - will be passed to :meth:`open`. + Optional parameters are passed to :meth:`open`. .. seealso:: :meth:`lines` """ - with self.open(mode='r', encoding=encoding, errors=errors) as f: - return U_NEWLINE.sub('\n', f.read()) + with self.open(encoding=encoding, errors=errors) as f: + return f.read() - def write_text(self, text, encoding=None, errors='strict', - linesep=os.linesep, append=False): - r""" Write the given text to this file. + def read_bytes(self): + r"""Return the contents of this file as bytes.""" + with self.open(mode='rb') as f: + return f.read() + + def text(self, encoding=None, errors='strict'): + r"""Legacy function to read text. + + Converts all newline sequences to ``\n``. + """ + warnings.warn(".text is deprecated; use read_text", DeprecationWarning) + return U_NEWLINE.sub('\n', self.read_text(encoding, errors)) + + def write_text( + self, text, encoding=None, errors='strict', linesep=os.linesep, append=False + ): + r"""Write the given text to this file. The default behavior is to overwrite any existing file; to append instead, use the `append=True` keyword argument. @@ -812,24 +688,22 @@ class Path(text_type): Parameters: - `text` - str/unicode - The text to be written. + `text` - str/bytes - The text to be written. - `encoding` - str - The Unicode encoding that will be used. - This is ignored if `text` isn't a Unicode string. + `encoding` - str - The text encoding used. `errors` - str - How to handle Unicode encoding errors. Default is ``'strict'``. See ``help(unicode.encode)`` for the - options. This is ignored if `text` isn't a Unicode - string. + options. Ignored if `text` isn't a Unicode string. `linesep` - keyword argument - str/unicode - The sequence of characters to be used to mark end-of-line. The default is - :data:`os.linesep`. You can also specify ``None`` to - leave all newlines as they are in `text`. + :data:`os.linesep`. Specify ``None`` to + use newlines unmodified. `append` - keyword argument - bool - Specifies what to do if the file already exists (``True``: append to the end of it; - ``False``: overwrite it.) The default is ``False``. + ``False``: overwrite it). The default is ``False``. --- Newline handling. @@ -839,18 +713,13 @@ class Path(text_type): end-of-line sequence (see :data:`os.linesep`; on Windows, for example, the end-of-line marker is ``'\r\n'``). - If you don't like your platform's default, you can override it - using the `linesep=` keyword argument. If you specifically want - ``write_text()`` to preserve the newlines as-is, use ``linesep=None``. - - This applies to Unicode text the same as to 8-bit text, except - there are three additional standard Unicode end-of-line sequences: - ``u'\x85'``, ``u'\r\x85'``, and ``u'\u2028'``. - - (This is slightly different from when you open a file for - writing with ``fopen(filename, "w")`` in C or ``open(filename, 'w')`` - in Python.) + To override the platform's default, pass the `linesep=` + keyword argument. To preserve the newlines as-is, pass + ``linesep=None``. + This handling applies to Unicode text and bytes, except + with Unicode, additional non-ASCII newlines are recognized: + ``\x85``, ``\r\x85``, and ``\u2028``. --- Unicode @@ -862,39 +731,52 @@ class Path(text_type): specified `encoding` (or the default encoding if `encoding` isn't specified). The `errors` argument applies only to this conversion. - """ - if isinstance(text, text_type): + if isinstance(text, str): if linesep is not None: text = U_NEWLINE.sub(linesep, text) - text = text.encode(encoding or sys.getdefaultencoding(), errors) + bytes = text.encode(encoding or sys.getdefaultencoding(), errors) else: + warnings.warn( + "Writing bytes in write_text is deprecated", + DeprecationWarning, + stacklevel=1, + ) assert encoding is None - text = NEWLINE.sub(linesep, text) - self.write_bytes(text, append=append) + if linesep is not None: + text = B_NEWLINE.sub(linesep.encode(), text) + bytes = text + self.write_bytes(bytes, append=append) - def lines(self, encoding=None, errors='strict', retain=True): - r""" Open this file, read all lines, return them in a list. + def lines(self, encoding=None, errors=None, retain=True): + r"""Open this file, read all lines, return them in a list. Optional arguments: `encoding` - The Unicode encoding (or character set) of - the file. The default is ``None``, meaning the content - of the file is read as 8-bit characters and returned - as a list of (non-Unicode) str objects. - `errors` - How to handle Unicode errors; see help(str.decode) - for the options. Default is ``'strict'``. - `retain` - If ``True``, retain newline characters; but all newline - character combinations (``'\r'``, ``'\n'``, ``'\r\n'``) are - translated to ``'\n'``. If ``False``, newline characters are - stripped off. Default is ``True``. + the file. The default is ``None``, meaning use + ``locale.getpreferredencoding()``. + `errors` - How to handle Unicode errors; see + `open `_ + for the options. Default is ``None`` meaning "strict". + `retain` - If ``True`` (default), retain newline characters, + but translate all newline + characters to ``\n``. If ``False``, newline characters are + omitted. .. seealso:: :meth:`text` """ - return self.text(encoding, errors).splitlines(retain) + text = U_NEWLINE.sub('\n', self.read_text(encoding, errors)) + return text.splitlines(retain) - def write_lines(self, lines, encoding=None, errors='strict', - linesep=os.linesep, append=False): - r""" Write the given lines of text to this file. + def write_lines( + self, + lines, + encoding=None, + errors='strict', + linesep=_default_linesep, + append=False, + ): + r"""Write the given lines of text to this file. By default this overwrites any existing file at this path. @@ -909,7 +791,7 @@ class Path(text_type): `errors` - How to handle errors in Unicode encoding. This also applies only to Unicode strings. - linesep - The desired line-ending. This line-ending is + linesep - (deprecated) The desired line-ending. This line-ending is applied to every line. If a line already has any standard line ending (``'\r'``, ``'\n'``, ``'\r\n'``, ``u'\x85'``, ``u'\r\x85'``, ``u'\u2028'``), that will @@ -917,32 +799,28 @@ class Path(text_type): default is os.linesep, which is platform-dependent (``'\r\n'`` on Windows, ``'\n'`` on Unix, etc.). Specify ``None`` to write the lines as-is, like - :meth:`file.writelines`. + ``.writelines`` on a file object. Use the keyword argument ``append=True`` to append lines to the file. The default is to overwrite the file. - - .. warning :: - - When you use this with Unicode data, if the encoding of the - existing data in the file is different from the encoding - you specify with the `encoding=` parameter, the result is - mixed-encoding data, which can really confuse someone trying - to read the file later. """ - with self.open('ab' if append else 'wb') as f: - for line in lines: - isUnicode = isinstance(line, text_type) - if linesep is not None: - pattern = U_NL_END if isUnicode else NL_END - line = pattern.sub('', line) + linesep - if isUnicode: - line = line.encode( - encoding or sys.getdefaultencoding(), errors) - f.write(line) + mode = 'a' if append else 'w' + with self.open(mode, encoding=encoding, errors=errors, newline='') as f: + f.writelines(self._replace_linesep(lines, linesep)) + + @staticmethod + def _replace_linesep(lines, linesep): + if linesep != _default_linesep: + warnings.warn("linesep is deprecated", DeprecationWarning, stacklevel=3) + else: + linesep = os.linesep + if linesep is None: + return lines + + return (line + linesep for line in _strip_newlines(lines)) def read_md5(self): - """ Calculate the md5 hash for this file. + """Calculate the md5 hash for this file. This reads through the entire file. @@ -951,7 +829,7 @@ class Path(text_type): return self.read_hash('md5') def _hash(self, hash_name): - """ Returns a hash object for the file at the current path. + """Returns a hash object for the file at the current path. `hash_name` should be a hash algo name (such as ``'md5'`` or ``'sha1'``) that's available in the :mod:`hashlib` module. @@ -962,7 +840,7 @@ class Path(text_type): return m def read_hash(self, hash_name): - """ Calculate given hash for this file. + """Calculate given hash for this file. List of supported hashes can be obtained from :mod:`hashlib` package. This reads the entire file. @@ -972,7 +850,7 @@ class Path(text_type): return self._hash(hash_name).digest() def read_hexhash(self, hash_name): - """ Calculate given hash for this file, returning hexdigest. + """Calculate given hash for this file, returning hexdigest. List of supported hashes can be obtained from :mod:`hashlib` package. This reads the entire file. @@ -987,121 +865,154 @@ class Path(text_type): # bound. Playing it safe and wrapping them all in method calls. def isabs(self): - """ .. seealso:: :func:`os.path.isabs` """ + """ + >>> Path('.').isabs() + False + + .. seealso:: :func:`os.path.isabs` + """ return self.module.isabs(self) def exists(self): - """ .. seealso:: :func:`os.path.exists` """ + """.. seealso:: :func:`os.path.exists`""" return self.module.exists(self) def isdir(self): - """ .. seealso:: :func:`os.path.isdir` """ + """.. seealso:: :func:`os.path.isdir`""" return self.module.isdir(self) def isfile(self): - """ .. seealso:: :func:`os.path.isfile` """ + """.. seealso:: :func:`os.path.isfile`""" return self.module.isfile(self) def islink(self): - """ .. seealso:: :func:`os.path.islink` """ + """.. seealso:: :func:`os.path.islink`""" return self.module.islink(self) def ismount(self): - """ .. seealso:: :func:`os.path.ismount` """ + """ + >>> Path('.').ismount() + False + + .. seealso:: :func:`os.path.ismount` + """ return self.module.ismount(self) def samefile(self, other): - """ .. seealso:: :func:`os.path.samefile` """ - if not hasattr(self.module, 'samefile'): - other = Path(other).realpath().normpath().normcase() - return self.realpath().normpath().normcase() == other + """.. seealso:: :func:`os.path.samefile`""" return self.module.samefile(self, other) def getatime(self): - """ .. seealso:: :attr:`atime`, :func:`os.path.getatime` """ + """.. seealso:: :attr:`atime`, :func:`os.path.getatime`""" return self.module.getatime(self) atime = property( - getatime, None, None, - """ Last access time of the file. + getatime, + None, + None, + """ + Last access time of the file. + + >>> Path('.').atime > 0 + True .. seealso:: :meth:`getatime`, :func:`os.path.getatime` - """) + """, + ) def getmtime(self): - """ .. seealso:: :attr:`mtime`, :func:`os.path.getmtime` """ + """.. seealso:: :attr:`mtime`, :func:`os.path.getmtime`""" return self.module.getmtime(self) mtime = property( - getmtime, None, None, - """ Last-modified time of the file. + getmtime, + None, + None, + """ + Last modified time of the file. .. seealso:: :meth:`getmtime`, :func:`os.path.getmtime` - """) + """, + ) def getctime(self): - """ .. seealso:: :attr:`ctime`, :func:`os.path.getctime` """ + """.. seealso:: :attr:`ctime`, :func:`os.path.getctime`""" return self.module.getctime(self) ctime = property( - getctime, None, None, + getctime, + None, + None, """ Creation time of the file. .. seealso:: :meth:`getctime`, :func:`os.path.getctime` - """) + """, + ) def getsize(self): - """ .. seealso:: :attr:`size`, :func:`os.path.getsize` """ + """.. seealso:: :attr:`size`, :func:`os.path.getsize`""" return self.module.getsize(self) size = property( - getsize, None, None, + getsize, + None, + None, """ Size of the file, in bytes. .. seealso:: :meth:`getsize`, :func:`os.path.getsize` - """) + """, + ) - if hasattr(os, 'access'): - def access(self, mode): - """ Return ``True`` if current user has access to this path. + def access(self, *args, **kwargs): + """ + Return does the real user have access to this path. - mode - One of the constants :data:`os.F_OK`, :data:`os.R_OK`, - :data:`os.W_OK`, :data:`os.X_OK` + >>> Path('.').access(os.F_OK) + True - .. seealso:: :func:`os.access` - """ - return os.access(self, mode) + .. seealso:: :func:`os.access` + """ + return os.access(self, *args, **kwargs) def stat(self): - """ Perform a ``stat()`` system call on this path. + """ + Perform a ``stat()`` system call on this path. + + >>> Path('.').stat() + os.stat_result(...) .. seealso:: :meth:`lstat`, :func:`os.stat` """ return os.stat(self) def lstat(self): - """ Like :meth:`stat`, but do not follow symbolic links. + """ + Like :meth:`stat`, but do not follow symbolic links. + + >>> Path('.').lstat() == Path('.').stat() + True .. seealso:: :meth:`stat`, :func:`os.lstat` """ return os.lstat(self) - def __get_owner_windows(self): - """ + def __get_owner_windows(self): # pragma: nocover + r""" Return the name of the owner of this file or directory. Follow symbolic links. - Return a name of the form ``r'DOMAIN\\User Name'``; may be a group. + Return a name of the form ``DOMAIN\User Name``; may be a group. .. seealso:: :attr:`owner` """ desc = win32security.GetFileSecurity( - self, win32security.OWNER_SECURITY_INFORMATION) + self, win32security.OWNER_SECURITY_INFORMATION + ) sid = desc.GetSecurityDescriptorOwner() account, domain, typecode = win32security.LookupAccountSid(None, sid) return domain + '\\' + account - def __get_owner_unix(self): + def __get_owner_unix(self): # pragma: nocover """ Return the name of the owner of this file or directory. Follow symbolic links. @@ -1111,44 +1022,50 @@ class Path(text_type): st = self.stat() return pwd.getpwuid(st.st_uid).pw_name - def __get_owner_not_implemented(self): + def __get_owner_not_implemented(self): # pragma: nocover raise NotImplementedError("Ownership not available on this platform.") - if 'win32security' in globals(): - get_owner = __get_owner_windows - elif 'pwd' in globals(): - get_owner = __get_owner_unix - else: - get_owner = __get_owner_not_implemented + get_owner = ( + __get_owner_windows + if 'win32security' in globals() + else __get_owner_unix + if 'pwd' in globals() + else __get_owner_not_implemented + ) owner = property( - get_owner, None, None, + get_owner, + None, + None, """ Name of the owner of this file or directory. - .. seealso:: :meth:`get_owner`""") + .. seealso:: :meth:`get_owner`""", + ) if hasattr(os, 'statvfs'): + def statvfs(self): - """ Perform a ``statvfs()`` system call on this path. + """Perform a ``statvfs()`` system call on this path. .. seealso:: :func:`os.statvfs` """ return os.statvfs(self) if hasattr(os, 'pathconf'): + def pathconf(self, name): - """ .. seealso:: :func:`os.pathconf` """ + """.. seealso:: :func:`os.pathconf`""" return os.pathconf(self, name) # # --- Modifying operations on files and directories - def utime(self, times): - """ Set the access and modified times of this file. + def utime(self, *args, **kwargs): + """Set the access and modified times of this file. .. seealso:: :func:`os.utime` """ - os.utime(self, times) + os.utime(self, *args, **kwargs) return self def chmod(self, mode): @@ -1158,36 +1075,37 @@ class Path(text_type): .. seealso:: :func:`os.chmod` """ - if isinstance(mode, string_types): - mask = _multi_permission_mask(mode) + if isinstance(mode, str): + mask = masks.compound(mode) mode = mask(self.stat().st_mode) os.chmod(self, mode) return self - def chown(self, uid=-1, gid=-1): - """ - Change the owner and group by names rather than the uid or gid numbers. + if hasattr(os, 'chown'): - .. seealso:: :func:`os.chown` - """ - if hasattr(os, 'chown'): - if 'pwd' in globals() and isinstance(uid, string_types): - uid = pwd.getpwnam(uid).pw_uid - if 'grp' in globals() and isinstance(gid, string_types): - gid = grp.getgrnam(gid).gr_gid - os.chown(self, uid, gid) - else: - msg = "Ownership not available on this platform." - raise NotImplementedError(msg) - return self + def chown(self, uid=-1, gid=-1): + """ + Change the owner and group by names or numbers. + + .. seealso:: :func:`os.chown` + """ + + def resolve_uid(uid): + return uid if isinstance(uid, int) else pwd.getpwnam(uid).pw_uid + + def resolve_gid(gid): + return gid if isinstance(gid, int) else grp.getgrnam(gid).gr_gid + + os.chown(self, resolve_uid(uid), resolve_gid(gid)) + return self def rename(self, new): - """ .. seealso:: :func:`os.rename` """ + """.. seealso:: :func:`os.rename`""" os.rename(self, new) return self._next_class(new) def renames(self, new): - """ .. seealso:: :func:`os.renames` """ + """.. seealso:: :func:`os.renames`""" os.renames(self, new) return self._next_class(new) @@ -1195,74 +1113,60 @@ class Path(text_type): # --- Create/delete operations on directories def mkdir(self, mode=0o777): - """ .. seealso:: :func:`os.mkdir` """ + """.. seealso:: :func:`os.mkdir`""" os.mkdir(self, mode) return self def mkdir_p(self, mode=0o777): - """ Like :meth:`mkdir`, but does not raise an exception if the - directory already exists. """ - try: + """Like :meth:`mkdir`, but does not raise an exception if the + directory already exists.""" + with contextlib.suppress(FileExistsError): self.mkdir(mode) - except OSError: - _, e, _ = sys.exc_info() - if e.errno != errno.EEXIST: - raise return self def makedirs(self, mode=0o777): - """ .. seealso:: :func:`os.makedirs` """ + """.. seealso:: :func:`os.makedirs`""" os.makedirs(self, mode) return self def makedirs_p(self, mode=0o777): - """ Like :meth:`makedirs`, but does not raise an exception if the - directory already exists. """ - try: + """Like :meth:`makedirs`, but does not raise an exception if the + directory already exists.""" + with contextlib.suppress(FileExistsError): self.makedirs(mode) - except OSError: - _, e, _ = sys.exc_info() - if e.errno != errno.EEXIST: - raise return self def rmdir(self): - """ .. seealso:: :func:`os.rmdir` """ + """.. seealso:: :func:`os.rmdir`""" os.rmdir(self) return self def rmdir_p(self): - """ Like :meth:`rmdir`, but does not raise an exception if the - directory is not empty or does not exist. """ - try: - self.rmdir() - except OSError: - _, e, _ = sys.exc_info() - bypass_codes = errno.ENOTEMPTY, errno.EEXIST, errno.ENOENT - if e.errno not in bypass_codes: - raise + """Like :meth:`rmdir`, but does not raise an exception if the + directory is not empty or does not exist.""" + suppressed = FileNotFoundError, FileExistsError, DirectoryNotEmpty + with contextlib.suppress(suppressed): + with DirectoryNotEmpty.translate(): + self.rmdir() return self def removedirs(self): - """ .. seealso:: :func:`os.removedirs` """ + """.. seealso:: :func:`os.removedirs`""" os.removedirs(self) return self def removedirs_p(self): - """ Like :meth:`removedirs`, but does not raise an exception if the - directory is not empty or does not exist. """ - try: - self.removedirs() - except OSError: - _, e, _ = sys.exc_info() - if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST: - raise + """Like :meth:`removedirs`, but does not raise an exception if the + directory is not empty or does not exist.""" + with contextlib.suppress(FileExistsError, DirectoryNotEmpty): + with DirectoryNotEmpty.translate(): + self.removedirs() return self # --- Modifying operations on files def touch(self): - """ Set the access/modified times of this file to the current time. + """Set the access/modified times of this file to the current time. Create the file if it does not exist. """ fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0o666) @@ -1271,78 +1175,61 @@ class Path(text_type): return self def remove(self): - """ .. seealso:: :func:`os.remove` """ + """.. seealso:: :func:`os.remove`""" os.remove(self) return self def remove_p(self): - """ Like :meth:`remove`, but does not raise an exception if the - file does not exist. """ - try: + """Like :meth:`remove`, but does not raise an exception if the + file does not exist.""" + with contextlib.suppress(FileNotFoundError): self.unlink() - except FileNotFoundError as exc: - if PY2 and exc.errno != errno.ENOENT: - raise return self - def unlink(self): - """ .. seealso:: :func:`os.unlink` """ - os.unlink(self) - return self - - def unlink_p(self): - """ Like :meth:`unlink`, but does not raise an exception if the - file does not exist. """ - self.remove_p() - return self + unlink = remove + unlink_p = remove_p # --- Links - if hasattr(os, 'link'): - def link(self, newpath): - """ Create a hard link at `newpath`, pointing to this file. + def link(self, newpath): + """Create a hard link at `newpath`, pointing to this file. - .. seealso:: :func:`os.link` - """ - os.link(self, newpath) - return self._next_class(newpath) + .. seealso:: :func:`os.link` + """ + os.link(self, newpath) + return self._next_class(newpath) - if hasattr(os, 'symlink'): - def symlink(self, newlink=None): - """ Create a symbolic link at `newlink`, pointing here. + def symlink(self, newlink=None): + """Create a symbolic link at `newlink`, pointing here. - If newlink is not supplied, the symbolic link will assume - the name self.basename(), creating the link in the cwd. + If newlink is not supplied, the symbolic link will assume + the name self.basename(), creating the link in the cwd. - .. seealso:: :func:`os.symlink` - """ - if newlink is None: - newlink = self.basename() - os.symlink(self, newlink) - return self._next_class(newlink) + .. seealso:: :func:`os.symlink` + """ + if newlink is None: + newlink = self.basename() + os.symlink(self, newlink) + return self._next_class(newlink) - if hasattr(os, 'readlink'): - def readlink(self): - """ Return the path to which this symbolic link points. + def readlink(self): + """Return the path to which this symbolic link points. - The result may be an absolute or a relative path. + The result may be an absolute or a relative path. - .. seealso:: :meth:`readlinkabs`, :func:`os.readlink` - """ - return self._next_class(os.readlink(self)) + .. seealso:: :meth:`readlinkabs`, :func:`os.readlink` + """ + return self._next_class(os.readlink(self)) - def readlinkabs(self): - """ Return the path to which this symbolic link points. + def readlinkabs(self): + """Return the path to which this symbolic link points. - The result is always an absolute path. + The result is always an absolute path. - .. seealso:: :meth:`readlink`, :func:`os.readlink` - """ - p = self.readlink() - if p.isabs(): - return p - else: - return (self.parent / p).abspath() + .. seealso:: :meth:`readlink`, :func:`os.readlink` + """ + p = self.readlink() + return p if p.isabs() else (self.parent / p).abspath() # High-level functions from shutil # These functions will be bound to the instance such that @@ -1359,28 +1246,26 @@ class Path(text_type): rmtree = shutil.rmtree def rmtree_p(self): - """ Like :meth:`rmtree`, but does not raise an exception if the - directory does not exist. """ - try: + """Like :meth:`rmtree`, but does not raise an exception if the + directory does not exist.""" + with contextlib.suppress(FileNotFoundError): self.rmtree() - except OSError: - _, e, _ = sys.exc_info() - if e.errno != errno.ENOENT: - raise return self def chdir(self): - """ .. seealso:: :func:`os.chdir` """ + """.. seealso:: :func:`os.chdir`""" os.chdir(self) cd = chdir def merge_tree( - self, dst, symlinks=False, - # * - update=False, - copy_function=shutil.copy2, - ignore=lambda dir, contents: []): + self, + dst, + symlinks=False, + *, + copy_function=shutil.copy2, + ignore=lambda dir, contents: [], + ): """ Copy entire contents of self to dst, overwriting existing contents in dst with those in self. @@ -1397,15 +1282,6 @@ class Path(text_type): dst = self._next_class(dst) dst.makedirs_p() - if update: - warnings.warn( - "Update is deprecated; " - "use copy_function=only_newer(shutil.copy2)", - DeprecationWarning, - stacklevel=2, - ) - copy_function = only_newer(copy_function) - sources = self.listdir() _ignored = ignore(self, [item.name for item in sources]) @@ -1421,7 +1297,6 @@ class Path(text_type): source.merge_tree( dest, symlinks=symlinks, - update=update, copy_function=copy_function, ignore=ignore, ) @@ -1434,22 +1309,29 @@ class Path(text_type): # --- Special stuff from os if hasattr(os, 'chroot'): - def chroot(self): - """ .. seealso:: :func:`os.chroot` """ + + def chroot(self): # pragma: nocover + """.. seealso:: :func:`os.chroot`""" os.chroot(self) if hasattr(os, 'startfile'): - def startfile(self): - """ .. seealso:: :func:`os.startfile` """ - os.startfile(self) + + def startfile(self, *args, **kwargs): # pragma: nocover + """.. seealso:: :func:`os.startfile`""" + os.startfile(self, *args, **kwargs) return self # in-place re-writing, courtesy of Martijn Pieters # http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ @contextlib.contextmanager def in_place( - self, mode='r', buffering=-1, encoding=None, errors=None, - newline=None, backup_extension=None, + self, + mode='r', + buffering=-1, + encoding=None, + errors=None, + newline=None, + backup_extension=None, ): """ A context in which a file may be re-written in-place with @@ -1476,68 +1358,62 @@ class Path(text_type): Thereafter, the file at `filename` will have line numbers in it. """ - import io - if set(mode).intersection('wa+'): raise ValueError('Only read-only file modes can be used') # move existing file to backup, create new file with same permissions # borrowed extensively from the fileinput module backup_fn = self + (backup_extension or os.extsep + 'bak') - try: - os.unlink(backup_fn) - except os.error: - pass - os.rename(self, backup_fn) + backup_fn.remove_p() + self.rename(backup_fn) readable = io.open( - backup_fn, mode, buffering=buffering, - encoding=encoding, errors=errors, newline=newline, + backup_fn, + mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, ) try: perm = os.fstat(readable.fileno()).st_mode except OSError: - writable = open( - self, 'w' + mode.replace('r', ''), - buffering=buffering, encoding=encoding, errors=errors, + writable = self.open( + 'w' + mode.replace('r', ''), + buffering=buffering, + encoding=encoding, + errors=errors, newline=newline, ) else: os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC - if hasattr(os, 'O_BINARY'): - os_mode |= os.O_BINARY + os_mode |= getattr(os, 'O_BINARY', 0) fd = os.open(self, os_mode, perm) writable = io.open( - fd, "w" + mode.replace('r', ''), - buffering=buffering, encoding=encoding, errors=errors, + fd, + "w" + mode.replace('r', ''), + buffering=buffering, + encoding=encoding, + errors=errors, newline=newline, ) - try: - if hasattr(os, 'chmod'): - os.chmod(self, perm) - except OSError: - pass + with contextlib.suppress(OSError, AttributeError): + self.chmod(perm) try: yield readable, writable except Exception: # move backup back readable.close() writable.close() - try: - os.unlink(self) - except os.error: - pass - os.rename(backup_fn, self) + self.remove_p() + backup_fn.rename(self) raise else: readable.close() writable.close() finally: - try: - os.unlink(backup_fn) - except os.error: - pass + backup_fn.remove_p() - @ClassProperty + @classes.ClassProperty @classmethod def special(cls): """ @@ -1563,24 +1439,64 @@ class Path(text_type): return functools.partial(SpecialResolver, cls) +class DirectoryNotEmpty(OSError): + @staticmethod + @contextlib.contextmanager + def translate(): + try: + yield + except OSError as exc: + if exc.errno == errno.ENOTEMPTY: + raise DirectoryNotEmpty(*exc.args) from exc + raise + + def only_newer(copy_func): """ Wrap a copy function (like shutil.copy2) to return the dst if it's newer than the source. """ + @functools.wraps(copy_func) def wrapper(src, dst, *args, **kwargs): - is_newer_dst = ( - dst.exists() - and dst.getmtime() >= src.getmtime() - ) + is_newer_dst = dst.exists() and dst.getmtime() >= src.getmtime() if is_newer_dst: return dst return copy_func(src, dst, *args, **kwargs) + return wrapper -class SpecialResolver(object): +class ExtantPath(Path): + """ + >>> ExtantPath('.') + ExtantPath('.') + >>> ExtantPath('does-not-exist') + Traceback (most recent call last): + OSError: does-not-exist does not exist. + """ + + def _validate(self): + if not self.exists(): + raise OSError(f"{self} does not exist.") + + +class ExtantFile(Path): + """ + >>> ExtantFile('.') + Traceback (most recent call last): + FileNotFoundError: . does not exist as a file. + >>> ExtantFile('does-not-exist') + Traceback (most recent call last): + FileNotFoundError: does-not-exist does not exist as a file. + """ + + def _validate(self): + if not self.isfile(): + raise FileNotFoundError(f"{self} does not exist as a file.") + + +class SpecialResolver: class ResolverScope: def __init__(self, paths, scope): self.paths = paths @@ -1592,13 +1508,8 @@ class SpecialResolver(object): def __init__(self, path_class, *args, **kwargs): appdirs = importlib.import_module('appdirs') - # let appname default to None until - # https://github.com/ActiveState/appdirs/issues/55 is solved. - not args and kwargs.setdefault('appname', None) - vars(self).update( - path_class=path_class, - wrapper=appdirs.AppDirs(*args, **kwargs), + path_class=path_class, wrapper=appdirs.AppDirs(*args, **kwargs) ) def __getattr__(self, scope): @@ -1619,11 +1530,10 @@ class Multi: """ A mix-in for a Path which may contain multiple Path separated by pathsep. """ + @classmethod def for_class(cls, path_cls): name = 'Multi' + path_cls.__name__ - if PY2: - name = str(name) return type(name, (cls, path_cls), {}) @classmethod @@ -1635,17 +1545,13 @@ class Multi: def __iter__(self): return iter(map(self._next_class, self.split(os.pathsep))) - @ClassProperty + @classes.ClassProperty @classmethod def _next_class(cls): """ Multi-subclasses should use the parent class """ - return next( - class_ - for class_ in cls.__mro__ - if not issubclass(class_, Multi) - ) + return next(class_ for class_ in cls.__mro__ if not issubclass(class_, Multi)) class TempDir(Path): @@ -1654,17 +1560,21 @@ class TempDir(Path): constructed with the same parameters that you can use as a context manager. - Example:: + For example: - with TempDir() as d: - # do stuff with the Path object "d" + >>> with TempDir() as d: + ... d.isdir() and isinstance(d, Path) + True - # here the directory is deleted automatically + The directory is deleted automatically. + + >>> d.isdir() + False .. seealso:: :func:`tempfile.mkdtemp` """ - @ClassProperty + @classes.ClassProperty @classmethod def _next_class(cls): return Path @@ -1684,138 +1594,21 @@ class TempDir(Path): return self._next_class(self) def __exit__(self, exc_type, exc_value, traceback): - if not exc_value: - self.rmtree() + self.rmtree() -# For backwards compatibility. -tempdir = TempDir +class Handlers: + def strict(msg): + raise + def warn(msg): + warnings.warn(msg, TreeWalkWarning) -def _multi_permission_mask(mode): - """ - Support multiple, comma-separated Unix chmod symbolic modes. + def ignore(msg): + pass - >>> _multi_permission_mask('a=r,u+w')(0) == 0o644 - True - """ - def compose(f, g): - return lambda *args, **kwargs: g(f(*args, **kwargs)) - return functools.reduce(compose, map(_permission_mask, mode.split(','))) - - -def _permission_mask(mode): - """ - Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function - suitable for applying to a mask to affect that change. - - >>> mask = _permission_mask('ugo+rwx') - >>> mask(0o554) == 0o777 - True - - >>> _permission_mask('go-x')(0o777) == 0o766 - True - - >>> _permission_mask('o-x')(0o445) == 0o444 - True - - >>> _permission_mask('a+x')(0) == 0o111 - True - - >>> _permission_mask('a=rw')(0o057) == 0o666 - True - - >>> _permission_mask('u=x')(0o666) == 0o166 - True - - >>> _permission_mask('g=')(0o157) == 0o107 - True - """ - # parse the symbolic mode - parsed = re.match('(?P[ugoa]+)(?P[-+=])(?P[rwx]*)$', mode) - if not parsed: - raise ValueError("Unrecognized symbolic mode", mode) - - # generate a mask representing the specified permission - spec_map = dict(r=4, w=2, x=1) - specs = (spec_map[perm] for perm in parsed.group('what')) - spec = functools.reduce(operator.or_, specs, 0) - - # now apply spec to each subject in who - shift_map = dict(u=6, g=3, o=0) - who = parsed.group('who').replace('a', 'ugo') - masks = (spec << shift_map[subj] for subj in who) - mask = functools.reduce(operator.or_, masks) - - op = parsed.group('op') - - # if op is -, invert the mask - if op == '-': - mask ^= 0o777 - - # if op is =, retain extant values for unreferenced subjects - if op == '=': - masks = (0o7 << shift_map[subj] for subj in who) - retain = functools.reduce(operator.or_, masks) ^ 0o777 - - op_map = { - '+': operator.or_, - '-': operator.and_, - '=': lambda mask, target: target & retain ^ mask, - } - return functools.partial(op_map[op], mask) - - -class CaseInsensitivePattern(matchers.CaseInsensitive): - def __init__(self, value): - warnings.warn( - "Use matchers.CaseInsensitive instead", - DeprecationWarning, - stacklevel=2, - ) - super(CaseInsensitivePattern, self).__init__(value) - - -class FastPath(Path): - def __init__(self, *args, **kwargs): - warnings.warn( - "Use Path, as FastPath no longer holds any advantage", - DeprecationWarning, - stacklevel=2, - ) - super(FastPath, self).__init__(*args, **kwargs) - - -def patch_for_linux_python2(): - """ - As reported in #130, when Linux users create filenames - not in the file system encoding, it creates problems on - Python 2. This function attempts to patch the os module - to make it behave more like that on Python 3. - """ - if not PY2 or platform.system() != 'Linux': - return - - try: - import backports.os - except ImportError: - return - - class OS: - """ - The proxy to the os module - """ - def __init__(self, wrapped): - self._orig = wrapped - - def __getattr__(self, name): - return getattr(self._orig, name) - - def listdir(self, *args, **kwargs): - items = self._orig.listdir(*args, **kwargs) - return list(map(backports.os.fsdecode, items)) - - globals().update(os=OS(os)) - - -patch_for_linux_python2() + @classmethod + def _resolve(cls, param): + if not callable(param) and param not in vars(Handlers): + raise ValueError("invalid errors parameter") + return vars(cls).get(param, param) diff --git a/libs/win/path/__init__.pyi b/libs/win/path/__init__.pyi new file mode 100644 index 00000000..a0b8f561 --- /dev/null +++ b/libs/win/path/__init__.pyi @@ -0,0 +1,483 @@ +from __future__ import annotations + +import builtins +import contextlib +import os +import shutil +import sys +from io import ( + BufferedRandom, + BufferedReader, + BufferedWriter, + FileIO, + TextIOWrapper, +) +from types import ModuleType, TracebackType +from typing import ( + Any, + AnyStr, + BinaryIO, + Callable, + Generator, + Iterable, + Iterator, + IO, + List, + Optional, + Set, + Tuple, + Type, + Union, + overload, +) + +from _typeshed import ( + OpenBinaryMode, + OpenBinaryModeUpdating, + OpenBinaryModeReading, + OpenBinaryModeWriting, + OpenTextMode, + Self, +) +from typing_extensions import Literal + +from . import classes + +# Type for the match argument for several methods +_Match = Optional[Union[str, Callable[[str], bool], Callable[[Path], bool]]] + +class TreeWalkWarning(Warning): + pass + +class Traversal: + follow: Callable[[Path], bool] + def __init__(self, follow: Callable[[Path], bool]): ... + def __call__( + self, + walker: Generator[Path, Optional[Callable[[], bool]], None], + ) -> Iterator[Path]: ... + +class Path(str): + module: Any + def __init__(self, other: Any = ...) -> None: ... + @classmethod + def using_module(cls, module: ModuleType) -> Type[Path]: ... + @classes.ClassProperty + @classmethod + def _next_class(cls: Type[Self]) -> Type[Self]: ... + def __repr__(self) -> str: ... + def __add__(self: Self, more: str) -> Self: ... + def __radd__(self: Self, other: str) -> Self: ... + def __div__(self: Self, rel: str) -> Self: ... + def __truediv__(self: Self, rel: str) -> Self: ... + def __rdiv__(self: Self, rel: str) -> Self: ... + def __rtruediv__(self: Self, rel: str) -> Self: ... + def __enter__(self: Self) -> Self: ... + def __exit__( + self, + exc_type: Optional[type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: ... + @classmethod + def getcwd(cls: Type[Self]) -> Self: ... + def abspath(self: Self) -> Self: ... + def normcase(self: Self) -> Self: ... + def normpath(self: Self) -> Self: ... + def realpath(self: Self) -> Self: ... + def expanduser(self: Self) -> Self: ... + def expandvars(self: Self) -> Self: ... + def dirname(self: Self) -> Self: ... + def basename(self: Self) -> Self: ... + def expand(self: Self) -> Self: ... + @property + def stem(self) -> str: ... + @property + def ext(self) -> str: ... + def with_suffix(self: Self, suffix: str) -> Self: ... + @property + def drive(self: Self) -> Self: ... + @property + def parent(self: Self) -> Self: ... + @property + def name(self: Self) -> Self: ... + def splitpath(self: Self) -> Tuple[Self, str]: ... + def splitdrive(self: Self) -> Tuple[Self, Self]: ... + def splitext(self: Self) -> Tuple[Self, str]: ... + def stripext(self: Self) -> Self: ... + @classes.multimethod + def joinpath(cls: Self, first: str, *others: str) -> Self: ... + def splitall(self: Self) -> List[Union[Self, str]]: ... + def parts(self: Self) -> Tuple[Union[Self, str], ...]: ... + def _parts(self: Self) -> Iterator[Union[Self, str]]: ... + def _parts_iter(self: Self) -> Iterator[Union[Self, str]]: ... + def relpath(self: Self, start: str = ...) -> Self: ... + def relpathto(self: Self, dest: str) -> Self: ... + # --- Listing, searching, walking, and matching + def listdir(self: Self, match: _Match = ...) -> List[Self]: ... + def dirs(self: Self, match: _Match = ...) -> List[Self]: ... + def files(self: Self, match: _Match = ...) -> List[Self]: ... + def walk( + self: Self, + match: _Match = ..., + errors: str = ..., + ) -> Generator[Self, Optional[Callable[[], bool]], None]: ... + def walkdirs( + self: Self, + match: _Match = ..., + errors: str = ..., + ) -> Iterator[Self]: ... + def walkfiles( + self: Self, + match: _Match = ..., + errors: str = ..., + ) -> Iterator[Self]: ... + def fnmatch( + self, + pattern: Union[Path, str], + normcase: Optional[Callable[[str], str]] = ..., + ) -> bool: ... + def glob(self: Self, pattern: str) -> List[Self]: ... + def iglob(self: Self, pattern: str) -> Iterator[Self]: ... + @overload + def open( + self, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Optional[Callable[[str, int], int]] = ..., + ) -> TextIOWrapper: ... + @overload + def open( + self, + mode: OpenBinaryMode, + buffering: Literal[0], + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> FileIO: ... + @overload + def open( + self, + mode: OpenBinaryModeUpdating, + buffering: Literal[-1, 1] = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> BufferedRandom: ... + @overload + def open( + self, + mode: OpenBinaryModeReading, + buffering: Literal[-1, 1] = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> BufferedReader: ... + @overload + def open( + self, + mode: OpenBinaryModeWriting, + buffering: Literal[-1, 1] = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> BufferedWriter: ... + @overload + def open( + self, + mode: OpenBinaryMode, + buffering: int, + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> BinaryIO: ... + @overload + def open( + self, + mode: str, + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Callable[[str, int], int] = ..., + ) -> IO[Any]: ... + def bytes(self) -> builtins.bytes: ... + @overload + def chunks( + self, + size: int, + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Optional[Callable[[str, int], int]] = ..., + ) -> Iterator[str]: ... + @overload + def chunks( + self, + size: int, + mode: OpenBinaryMode, + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Optional[Callable[[str, int], int]] = ..., + ) -> Iterator[builtins.bytes]: ... + @overload + def chunks( + self, + size: int, + mode: str, + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + closefd: bool = ..., + opener: Optional[Callable[[str, int], int]] = ..., + ) -> Iterator[Union[str, builtins.bytes]]: ... + def write_bytes(self, bytes: builtins.bytes, append: bool = ...) -> None: ... + def read_text( + self, encoding: Optional[str] = ..., errors: Optional[str] = ... + ) -> str: ... + def read_bytes(self) -> builtins.bytes: ... + def text(self, encoding: Optional[str] = ..., errors: str = ...) -> str: ... + @overload + def write_text( + self, + text: str, + encoding: Optional[str] = ..., + errors: str = ..., + linesep: Optional[str] = ..., + append: bool = ..., + ) -> None: ... + @overload + def write_text( + self, + text: builtins.bytes, + encoding: None = ..., + errors: str = ..., + linesep: Optional[str] = ..., + append: bool = ..., + ) -> None: ... + def lines( + self, + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + retain: bool = ..., + ) -> List[str]: ... + def write_lines( + self, + lines: List[str], + encoding: Optional[str] = ..., + errors: str = ..., + linesep: Optional[str] = ..., + append: bool = ..., + ) -> None: ... + def read_md5(self) -> builtins.bytes: ... + def read_hash(self, hash_name: str) -> builtins.bytes: ... + def read_hexhash(self, hash_name: str) -> str: ... + def isabs(self) -> bool: ... + def exists(self) -> bool: ... + def isdir(self) -> bool: ... + def isfile(self) -> bool: ... + def islink(self) -> bool: ... + def ismount(self) -> bool: ... + def samefile(self, other: str) -> bool: ... + def getatime(self) -> float: ... + @property + def atime(self) -> float: ... + def getmtime(self) -> float: ... + @property + def mtime(self) -> float: ... + def getctime(self) -> float: ... + @property + def ctime(self) -> float: ... + def getsize(self) -> int: ... + @property + def size(self) -> int: ... + def access( + self, + mode: int, + *, + dir_fd: Optional[int] = ..., + effective_ids: bool = ..., + follow_symlinks: bool = ..., + ) -> bool: ... + def stat(self) -> os.stat_result: ... + def lstat(self) -> os.stat_result: ... + def get_owner(self) -> str: ... + @property + def owner(self) -> str: ... + if sys.platform != 'win32': + def statvfs(self) -> os.statvfs_result: ... + def pathconf(self, name: Union[str, int]) -> int: ... + + def utime( + self, + times: Union[Tuple[int, int], Tuple[float, float], None] = ..., + *, + ns: Tuple[int, int] = ..., + dir_fd: Optional[int] = ..., + follow_symlinks: bool = ..., + ) -> Path: ... + def chmod(self: Self, mode: Union[str, int]) -> Self: ... + if sys.platform != 'win32': + def chown( + self: Self, uid: Union[int, str] = ..., gid: Union[int, str] = ... + ) -> Self: ... + + def rename(self: Self, new: str) -> Self: ... + def renames(self: Self, new: str) -> Self: ... + def mkdir(self: Self, mode: int = ...) -> Self: ... + def mkdir_p(self: Self, mode: int = ...) -> Self: ... + def makedirs(self: Self, mode: int = ...) -> Self: ... + def makedirs_p(self: Self, mode: int = ...) -> Self: ... + def rmdir(self: Self) -> Self: ... + def rmdir_p(self: Self) -> Self: ... + def removedirs(self: Self) -> Self: ... + def removedirs_p(self: Self) -> Self: ... + def touch(self: Self) -> Self: ... + def remove(self: Self) -> Self: ... + def remove_p(self: Self) -> Self: ... + def unlink(self: Self) -> Self: ... + def unlink_p(self: Self) -> Self: ... + def link(self: Self, newpath: str) -> Self: ... + def symlink(self: Self, newlink: Optional[str] = ...) -> Self: ... + def readlink(self: Self) -> Self: ... + def readlinkabs(self: Self) -> Self: ... + def copyfile(self, dst: str, *, follow_symlinks: bool = ...) -> str: ... + def copymode(self, dst: str, *, follow_symlinks: bool = ...) -> None: ... + def copystat(self, dst: str, *, follow_symlinks: bool = ...) -> None: ... + def copy(self, dst: str, *, follow_symlinks: bool = ...) -> Any: ... + def copy2(self, dst: str, *, follow_symlinks: bool = ...) -> Any: ... + def copytree( + self, + dst: str, + symlinks: bool = ..., + ignore: Optional[Callable[[str, list[str]], Iterable[str]]] = ..., + copy_function: Callable[[str, str], None] = ..., + ignore_dangling_symlinks: bool = ..., + dirs_exist_ok: bool = ..., + ) -> Any: ... + def move( + self, dst: str, copy_function: Callable[[str, str], None] = ... + ) -> Any: ... + def rmtree( + self, + ignore_errors: bool = ..., + onerror: Optional[Callable[[Any, Any, Any], Any]] = ..., + ) -> None: ... + def rmtree_p(self: Self) -> Self: ... + def chdir(self) -> None: ... + def cd(self) -> None: ... + def merge_tree( + self, + dst: str, + symlinks: bool = ..., + *, + copy_function: Callable[[str, str], None] = ..., + ignore: Callable[[Any, List[str]], Union[List[str], Set[str]]] = ..., + ) -> None: ... + if sys.platform != 'win32': + def chroot(self) -> None: ... + if sys.platform == 'win32': + def startfile(self: Self, operation: Optional[str] = ...) -> Self: ... + + @contextlib.contextmanager + def in_place( + self, + mode: str = ..., + buffering: int = ..., + encoding: Optional[str] = ..., + errors: Optional[str] = ..., + newline: Optional[str] = ..., + backup_extension: Optional[str] = ..., + ) -> Iterator[Tuple[IO[Any], IO[Any]]]: ... + @classes.ClassProperty + @classmethod + def special(cls) -> Callable[[Optional[str]], SpecialResolver]: ... + +class DirectoryNotEmpty(OSError): + @staticmethod + def translate() -> Iterator[None]: ... + +def only_newer(copy_func: Callable[[str, str], None]) -> Callable[[str, str], None]: ... + +class ExtantPath(Path): + def _validate(self) -> None: ... + +class ExtantFile(Path): + def _validate(self) -> None: ... + +class SpecialResolver: + class ResolverScope: + def __init__(self, paths: SpecialResolver, scope: str) -> None: ... + def __getattr__(self, class_: str) -> MultiPathType: ... + + def __init__( + self, + path_class: type, + appname: Optional[str] = ..., + appauthor: Optional[str] = ..., + version: Optional[str] = ..., + roaming: bool = ..., + multipath: bool = ..., + ): ... + def __getattr__(self, scope: str) -> ResolverScope: ... + def get_dir(self, scope: str, class_: str) -> MultiPathType: ... + +class Multi: + @classmethod + def for_class(cls, path_cls: type) -> Type[MultiPathType]: ... + @classmethod + def detect(cls, input: str) -> MultiPathType: ... + def __iter__(self) -> Iterator[Path]: ... + @classes.ClassProperty + @classmethod + def _next_class(cls) -> Type[Path]: ... + +class MultiPathType(Multi, Path): + pass + +class TempDir(Path): + @classes.ClassProperty + @classmethod + def _next_class(cls) -> Type[Path]: ... + def __new__( + cls: Type[Self], + suffix: Optional[AnyStr] = ..., + prefix: Optional[AnyStr] = ..., + dir: Optional[Union[AnyStr, os.PathLike[AnyStr]]] = ..., + ) -> Self: ... + def __init__(self) -> None: ... + def __enter__(self) -> Path: ... # type: ignore + def __exit__( + self, + exc_type: Optional[type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: ... + +class Handlers: + @classmethod + def _resolve( + cls, param: Union[str, Callable[[str], None]] + ) -> Callable[[str], None]: ... diff --git a/libs/win/path/classes.py b/libs/win/path/classes.py new file mode 100644 index 00000000..b6101d0a --- /dev/null +++ b/libs/win/path/classes.py @@ -0,0 +1,27 @@ +import functools + + +class ClassProperty(property): + def __get__(self, cls, owner): + return self.fget.__get__(None, owner)() + + +class multimethod: + """ + Acts like a classmethod when invoked from the class and like an + instancemethod when invoked from the instance. + """ + + def __init__(self, func): + self.func = func + + def __get__(self, instance, owner): + """ + If called on an instance, pass the instance as the first + argument. + """ + return ( + functools.partial(self.func, owner) + if instance is None + else functools.partial(self.func, owner, instance) + ) diff --git a/libs/win/path/classes.pyi b/libs/win/path/classes.pyi new file mode 100644 index 00000000..2878c48b --- /dev/null +++ b/libs/win/path/classes.pyi @@ -0,0 +1,8 @@ +from typing import Any, Callable, Optional + +class ClassProperty(property): + def __get__(self, cls: Any, owner: Optional[type] = ...) -> Any: ... + +class multimethod: + def __init__(self, func: Callable[..., Any]): ... + def __get__(self, instance: Any, owner: Optional[type]) -> Any: ... diff --git a/libs/win/path/masks.py b/libs/win/path/masks.py new file mode 100644 index 00000000..761e51f8 --- /dev/null +++ b/libs/win/path/masks.py @@ -0,0 +1,85 @@ +import re +import functools +import operator + + +# from jaraco.functools +def compose(*funcs): + compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs)) # noqa + return functools.reduce(compose_two, funcs) + + +def compound(mode): + """ + Support multiple, comma-separated Unix chmod symbolic modes. + + >>> oct(compound('a=r,u+w')(0)) + '0o644' + """ + return compose(*map(simple, reversed(mode.split(',')))) + + +def simple(mode): + """ + Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function + suitable for applying to a mask to affect that change. + + >>> mask = simple('ugo+rwx') + >>> mask(0o554) == 0o777 + True + + >>> simple('go-x')(0o777) == 0o766 + True + + >>> simple('o-x')(0o445) == 0o444 + True + + >>> simple('a+x')(0) == 0o111 + True + + >>> simple('a=rw')(0o057) == 0o666 + True + + >>> simple('u=x')(0o666) == 0o166 + True + + >>> simple('g=')(0o157) == 0o107 + True + + >>> simple('gobbledeegook') + Traceback (most recent call last): + ValueError: ('Unrecognized symbolic mode', 'gobbledeegook') + """ + # parse the symbolic mode + parsed = re.match('(?P[ugoa]+)(?P[-+=])(?P[rwx]*)$', mode) + if not parsed: + raise ValueError("Unrecognized symbolic mode", mode) + + # generate a mask representing the specified permission + spec_map = dict(r=4, w=2, x=1) + specs = (spec_map[perm] for perm in parsed.group('what')) + spec = functools.reduce(operator.or_, specs, 0) + + # now apply spec to each subject in who + shift_map = dict(u=6, g=3, o=0) + who = parsed.group('who').replace('a', 'ugo') + masks = (spec << shift_map[subj] for subj in who) + mask = functools.reduce(operator.or_, masks) + + op = parsed.group('op') + + # if op is -, invert the mask + if op == '-': + mask ^= 0o777 + + # if op is =, retain extant values for unreferenced subjects + if op == '=': + masks = (0o7 << shift_map[subj] for subj in who) + retain = functools.reduce(operator.or_, masks) ^ 0o777 + + op_map = { + '+': operator.or_, + '-': operator.and_, + '=': lambda mask, target: target & retain ^ mask, + } + return functools.partial(op_map[op], mask) diff --git a/libs/win/path/masks.pyi b/libs/win/path/masks.pyi new file mode 100644 index 00000000..d69bf202 --- /dev/null +++ b/libs/win/path/masks.pyi @@ -0,0 +1,5 @@ +from typing import Any, Callable + +def compose(*funcs: Callable[..., Any]) -> Callable[..., Any]: ... +def compound(mode: str) -> Callable[[int], int]: ... +def simple(mode: str) -> Callable[[int], int]: ... diff --git a/libs/win/path/matchers.py b/libs/win/path/matchers.py new file mode 100644 index 00000000..63ca218a --- /dev/null +++ b/libs/win/path/matchers.py @@ -0,0 +1,59 @@ +import ntpath +import fnmatch + + +def load(param): + """ + If the supplied parameter is a string, assume it's a simple + pattern. + """ + return ( + Pattern(param) + if isinstance(param, str) + else param + if param is not None + else Null() + ) + + +class Base: + pass + + +class Null(Base): + def __call__(self, path): + return True + + +class Pattern(Base): + def __init__(self, pattern): + self.pattern = pattern + + def get_pattern(self, normcase): + try: + return self._pattern + except AttributeError: + pass + self._pattern = normcase(self.pattern) + return self._pattern + + def __call__(self, path): + normcase = getattr(self, 'normcase', path.module.normcase) + pattern = self.get_pattern(normcase) + return fnmatch.fnmatchcase(normcase(path.name), pattern) + + +class CaseInsensitive(Pattern): + """ + A Pattern with a ``'normcase'`` property, suitable for passing to + :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`, + :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive. + + For example, to get all files ending in .py, .Py, .pY, or .PY in the + current directory:: + + from path import Path, matchers + Path('.').files(matchers.CaseInsensitive('*.py')) + """ + + normcase = staticmethod(ntpath.normcase) diff --git a/libs/win/path/matchers.pyi b/libs/win/path/matchers.pyi new file mode 100644 index 00000000..80acd0b1 --- /dev/null +++ b/libs/win/path/matchers.pyi @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import Any, Callable, overload + +from typing_extensions import Literal + +from path import Path + +@overload +def load(param: None) -> Null: ... +@overload +def load(param: str) -> Pattern: ... +@overload +def load(param: Any) -> Any: ... + +class Base: + pass + +class Null(Base): + def __call__(self, path: str) -> Literal[True]: ... + +class Pattern(Base): + def __init__(self, pattern: str) -> None: ... + def get_pattern(self, normcase: Callable[[str], str]) -> str: ... + def __call__(self, path: Path) -> bool: ... + +class CaseInsensitive(Pattern): + normcase: Callable[[str], str] diff --git a/libs/win/path/py.typed b/libs/win/path/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/path/py37compat.py b/libs/win/path/py37compat.py new file mode 100644 index 00000000..f2a9e8b4 --- /dev/null +++ b/libs/win/path/py37compat.py @@ -0,0 +1,125 @@ +import functools +import os + + +def best_realpath(module): + """ + Given a path module (i.e. ntpath, posixpath), + determine the best 'realpath' function to use + for best future compatibility. + """ + needs_backport = module.realpath is module.abspath + return realpath_backport if needs_backport else module.realpath + + +# backport taken from jaraco.windows 5 +def realpath_backport(path): + if isinstance(path, str): + prefix = '\\\\?\\' + unc_prefix = prefix + 'UNC' + new_unc_prefix = '\\' + cwd = os.getcwd() + else: + prefix = b'\\\\?\\' + unc_prefix = prefix + b'UNC' + new_unc_prefix = b'\\' + cwd = os.getcwdb() + had_prefix = path.startswith(prefix) + path, ok = _resolve_path(cwd, path, {}) + # The path returned by _getfinalpathname will always start with \\?\ - + # strip off that prefix unless it was already provided on the original + # path. + if not had_prefix: + # For UNC paths, the prefix will actually be \\?\UNC - handle that + # case as well. + if path.startswith(unc_prefix): + path = new_unc_prefix + path[len(unc_prefix) :] + elif path.startswith(prefix): + path = path[len(prefix) :] + return path + + +def _resolve_path(path, rest, seen): # noqa: C901 + # Windows normalizes the path before resolving symlinks; be sure to + # follow the same behavior. + rest = os.path.normpath(rest) + + if isinstance(rest, str): + sep = '\\' + else: + sep = b'\\' + + if os.path.isabs(rest): + drive, rest = os.path.splitdrive(rest) + path = drive + sep + rest = rest[1:] + + while rest: + name, _, rest = rest.partition(sep) + new_path = os.path.join(path, name) if path else name + if os.path.exists(new_path): + if not rest: + # The whole path exists. Resolve it using the OS. + path = os.path._getfinalpathname(new_path) + else: + # The OS can resolve `new_path`; keep traversing the path. + path = new_path + elif not os.path.lexists(new_path): + # `new_path` does not exist on the filesystem at all. Use the + # OS to resolve `path`, if it exists, and then append the + # remainder. + if os.path.exists(path): + path = os.path._getfinalpathname(path) + rest = os.path.join(name, rest) if rest else name + return os.path.join(path, rest), True + else: + # We have a symbolic link that the OS cannot resolve. Try to + # resolve it ourselves. + + # On Windows, symbolic link resolution can be partially or + # fully disabled [1]. The end result of a disabled symlink + # appears the same as a broken symlink (lexists() returns True + # but exists() returns False). And in both cases, the link can + # still be read using readlink(). Call stat() and check the + # resulting error code to ensure we don't circumvent the + # Windows symbolic link restrictions. + # [1] https://technet.microsoft.com/en-us/library/cc754077.aspx + try: + os.stat(new_path) + except OSError as e: + # WinError 1463: The symbolic link cannot be followed + # because its type is disabled. + if e.winerror == 1463: + raise + + key = os.path.normcase(new_path) + if key in seen: + # This link has already been seen; try to use the + # previously resolved value. + path = seen[key] + if path is None: + # It has not yet been resolved, which means we must + # have a symbolic link loop. Return what we have + # resolved so far plus the remainder of the path (who + # cares about the Zen of Python?). + path = os.path.join(new_path, rest) if rest else new_path + return path, False + else: + # Mark this link as in the process of being resolved. + seen[key] = None + # Try to resolve it. + path, ok = _resolve_path(path, os.readlink(new_path), seen) + if ok: + # Resolution succeded; store the resolved value. + seen[key] = path + else: + # Resolution failed; punt. + return (os.path.join(path, rest) if rest else path), False + return path, True + + +def lru_cache(user_function): + """ + Support for lru_cache(user_function) + """ + return functools.lru_cache()(user_function) diff --git a/libs/win/path/py37compat.pyi b/libs/win/path/py37compat.pyi new file mode 100644 index 00000000..ea62fa06 --- /dev/null +++ b/libs/win/path/py37compat.pyi @@ -0,0 +1,17 @@ +import os + +from types import ModuleType +from typing import Any, AnyStr, Callable, Dict, Tuple, overload + +def best_realpath(module: ModuleType) -> Callable[[AnyStr], AnyStr]: ... +@overload +def realpath_backport(path: str) -> str: ... +@overload +def realpath_backport(path: bytes) -> bytes: ... +@overload +def _resolve_path(path: str, rest: str, seen: Dict[Any, Any]) -> Tuple[str, bool]: ... +@overload +def _resolve_path( + path: bytes, rest: bytes, seen: Dict[Any, Any] +) -> Tuple[bytes, bool]: ... +def lru_cache(user_function: Callable[..., Any]) -> Callable[..., Any]: ... diff --git a/libs/win/pydantic/__init__.cp37-win_amd64.pyd b/libs/win/pydantic/__init__.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..e6293456ada79c2dd4c89e5bfd1bced6ede87b50 GIT binary patch literal 34304 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/__init__.py b/libs/win/pydantic/__init__.py new file mode 100644 index 00000000..3bf1418f --- /dev/null +++ b/libs/win/pydantic/__init__.py @@ -0,0 +1,131 @@ +# flake8: noqa +from . import dataclasses +from .annotated_types import create_model_from_namedtuple, create_model_from_typeddict +from .class_validators import root_validator, validator +from .config import BaseConfig, ConfigDict, Extra +from .decorator import validate_arguments +from .env_settings import BaseSettings +from .error_wrappers import ValidationError +from .errors import * +from .fields import Field, PrivateAttr, Required +from .main import * +from .networks import * +from .parse import Protocol +from .tools import * +from .types import * +from .version import VERSION, compiled + +__version__ = VERSION + +# WARNING __all__ from .errors is not included here, it will be removed as an export here in v2 +# please use "from pydantic.errors import ..." instead +__all__ = [ + # annotated types utils + 'create_model_from_namedtuple', + 'create_model_from_typeddict', + # dataclasses + 'dataclasses', + # class_validators + 'root_validator', + 'validator', + # config + 'BaseConfig', + 'ConfigDict', + 'Extra', + # decorator + 'validate_arguments', + # env_settings + 'BaseSettings', + # error_wrappers + 'ValidationError', + # fields + 'Field', + 'Required', + # main + 'BaseModel', + 'create_model', + 'validate_model', + # network + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'HttpUrl', + 'stricturl', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'validate_email', + # parse + 'Protocol', + # tools + 'parse_file_as', + 'parse_obj_as', + 'parse_raw_as', + 'schema_of', + 'schema_json_of', + # types + 'NoneStr', + 'NoneBytes', + 'StrBytes', + 'NoneStrBytes', + 'StrictStr', + 'ConstrainedBytes', + 'conbytes', + 'ConstrainedList', + 'conlist', + 'ConstrainedSet', + 'conset', + 'ConstrainedFrozenSet', + 'confrozenset', + 'ConstrainedStr', + 'constr', + 'PyObject', + 'ConstrainedInt', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'ConstrainedFloat', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'ConstrainedDecimal', + 'condecimal', + 'ConstrainedDate', + 'condate', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'Json', + 'JsonWrapper', + 'SecretField', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'PrivateAttr', + 'ByteSize', + 'PastDate', + 'FutureDate', + # version + 'compiled', + 'VERSION', +] diff --git a/libs/win/pydantic/_hypothesis_plugin.cp37-win_amd64.pyd b/libs/win/pydantic/_hypothesis_plugin.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..480ba0ae985cf9c9d98794f3c1fb26b2193b7316 GIT binary patch literal 154112 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/_hypothesis_plugin.py b/libs/win/pydantic/_hypothesis_plugin.py new file mode 100644 index 00000000..a56d2b98 --- /dev/null +++ b/libs/win/pydantic/_hypothesis_plugin.py @@ -0,0 +1,386 @@ +""" +Register Hypothesis strategies for Pydantic custom types. + +This enables fully-automatic generation of test data for most Pydantic classes. + +Note that this module has *no* runtime impact on Pydantic itself; instead it +is registered as a setuptools entry point and Hypothesis will import it if +Pydantic is installed. See also: + +https://hypothesis.readthedocs.io/en/latest/strategies.html#registering-strategies-via-setuptools-entry-points +https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.register_type_strategy +https://hypothesis.readthedocs.io/en/latest/strategies.html#interaction-with-pytest-cov +https://pydantic-docs.helpmanual.io/usage/types/#pydantic-types + +Note that because our motivation is to *improve user experience*, the strategies +are always sound (never generate invalid data) but sacrifice completeness for +maintainability (ie may be unable to generate some tricky but valid data). + +Finally, this module makes liberal use of `# type: ignore[]` pragmas. +This is because Hypothesis annotates `register_type_strategy()` with +`(T, SearchStrategy[T])`, but in most cases we register e.g. `ConstrainedInt` +to generate instances of the builtin `int` type which match the constraints. +""" + +import contextlib +import datetime +import ipaddress +import json +import math +from fractions import Fraction +from typing import Callable, Dict, Type, Union, cast, overload + +import hypothesis.strategies as st + +import pydantic +import pydantic.color +import pydantic.types +from pydantic.utils import lenient_issubclass + +# FilePath and DirectoryPath are explicitly unsupported, as we'd have to create +# them on-disk, and that's unsafe in general without being told *where* to do so. +# +# URLs are unsupported because it's easy for users to define their own strategy for +# "normal" URLs, and hard for us to define a general strategy which includes "weird" +# URLs but doesn't also have unpredictable performance problems. +# +# conlist() and conset() are unsupported for now, because the workarounds for +# Cython and Hypothesis to handle parametrized generic types are incompatible. +# Once Cython can support 'normal' generics we'll revisit this. + +# Emails +try: + import email_validator +except ImportError: # pragma: no cover + pass +else: + + def is_valid_email(s: str) -> bool: + # Hypothesis' st.emails() occasionally generates emails like 0@A0--0.ac + # that are invalid according to email-validator, so we filter those out. + try: + email_validator.validate_email(s, check_deliverability=False) + return True + except email_validator.EmailNotValidError: # pragma: no cover + return False + + # Note that these strategies deliberately stay away from any tricky Unicode + # or other encoding issues; we're just trying to generate *something* valid. + st.register_type_strategy(pydantic.EmailStr, st.emails().filter(is_valid_email)) # type: ignore[arg-type] + st.register_type_strategy( + pydantic.NameEmail, + st.builds( + '{} <{}>'.format, # type: ignore[arg-type] + st.from_regex('[A-Za-z0-9_]+( [A-Za-z0-9_]+){0,5}', fullmatch=True), + st.emails().filter(is_valid_email), + ), + ) + +# PyObject - dotted names, in this case taken from the math module. +st.register_type_strategy( + pydantic.PyObject, # type: ignore[arg-type] + st.sampled_from( + [cast(pydantic.PyObject, f'math.{name}') for name in sorted(vars(math)) if not name.startswith('_')] + ), +) + +# CSS3 Colors; as name, hex, rgb(a) tuples or strings, or hsl strings +_color_regexes = ( + '|'.join( + ( + pydantic.color.r_hex_short, + pydantic.color.r_hex_long, + pydantic.color.r_rgb, + pydantic.color.r_rgba, + pydantic.color.r_hsl, + pydantic.color.r_hsla, + ) + ) + # Use more precise regex patterns to avoid value-out-of-range errors + .replace(pydantic.color._r_sl, r'(?:(\d\d?(?:\.\d+)?|100(?:\.0+)?)%)') + .replace(pydantic.color._r_alpha, r'(?:(0(?:\.\d+)?|1(?:\.0+)?|\.\d+|\d{1,2}%))') + .replace(pydantic.color._r_255, r'(?:((?:\d|\d\d|[01]\d\d|2[0-4]\d|25[0-4])(?:\.\d+)?|255(?:\.0+)?))') +) +st.register_type_strategy( + pydantic.color.Color, + st.one_of( + st.sampled_from(sorted(pydantic.color.COLORS_BY_NAME)), + st.tuples( + st.integers(0, 255), + st.integers(0, 255), + st.integers(0, 255), + st.none() | st.floats(0, 1) | st.floats(0, 100).map('{}%'.format), + ), + st.from_regex(_color_regexes, fullmatch=True), + ), +) + + +# Card numbers, valid according to the Luhn algorithm + + +def add_luhn_digit(card_number: str) -> str: + # See https://en.wikipedia.org/wiki/Luhn_algorithm + for digit in '0123456789': + with contextlib.suppress(Exception): + pydantic.PaymentCardNumber.validate_luhn_check_digit(card_number + digit) + return card_number + digit + raise AssertionError('Unreachable') # pragma: no cover + + +card_patterns = ( + # Note that these patterns omit the Luhn check digit; that's added by the function above + '4[0-9]{14}', # Visa + '5[12345][0-9]{13}', # Mastercard + '3[47][0-9]{12}', # American Express + '[0-26-9][0-9]{10,17}', # other (incomplete to avoid overlap) +) +st.register_type_strategy( + pydantic.PaymentCardNumber, + st.from_regex('|'.join(card_patterns), fullmatch=True).map(add_luhn_digit), # type: ignore[arg-type] +) + +# UUIDs +st.register_type_strategy(pydantic.UUID1, st.uuids(version=1)) +st.register_type_strategy(pydantic.UUID3, st.uuids(version=3)) +st.register_type_strategy(pydantic.UUID4, st.uuids(version=4)) +st.register_type_strategy(pydantic.UUID5, st.uuids(version=5)) + +# Secrets +st.register_type_strategy(pydantic.SecretBytes, st.binary().map(pydantic.SecretBytes)) +st.register_type_strategy(pydantic.SecretStr, st.text().map(pydantic.SecretStr)) + +# IP addresses, networks, and interfaces +st.register_type_strategy(pydantic.IPvAnyAddress, st.ip_addresses()) # type: ignore[arg-type] +st.register_type_strategy( + pydantic.IPvAnyInterface, + st.from_type(ipaddress.IPv4Interface) | st.from_type(ipaddress.IPv6Interface), # type: ignore[arg-type] +) +st.register_type_strategy( + pydantic.IPvAnyNetwork, + st.from_type(ipaddress.IPv4Network) | st.from_type(ipaddress.IPv6Network), # type: ignore[arg-type] +) + +# We hook into the con***() functions and the ConstrainedNumberMeta metaclass, +# so here we only have to register subclasses for other constrained types which +# don't go via those mechanisms. Then there are the registration hooks below. +st.register_type_strategy(pydantic.StrictBool, st.booleans()) +st.register_type_strategy(pydantic.StrictStr, st.text()) + + +# Constrained-type resolver functions +# +# For these ones, we actually want to inspect the type in order to work out a +# satisfying strategy. First up, the machinery for tracking resolver functions: + +RESOLVERS: Dict[type, Callable[[type], st.SearchStrategy]] = {} # type: ignore[type-arg] + + +@overload +def _registered(typ: Type[pydantic.types.T]) -> Type[pydantic.types.T]: + pass + + +@overload +def _registered(typ: pydantic.types.ConstrainedNumberMeta) -> pydantic.types.ConstrainedNumberMeta: + pass + + +def _registered( + typ: Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta] +) -> Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta]: + # This function replaces the version in `pydantic.types`, in order to + # effect the registration of new constrained types so that Hypothesis + # can generate valid examples. + pydantic.types._DEFINED_TYPES.add(typ) + for supertype, resolver in RESOLVERS.items(): + if issubclass(typ, supertype): + st.register_type_strategy(typ, resolver(typ)) # type: ignore + return typ + raise NotImplementedError(f'Unknown type {typ!r} has no resolver to register') # pragma: no cover + + +def resolves( + typ: Union[type, pydantic.types.ConstrainedNumberMeta] +) -> Callable[[Callable[..., st.SearchStrategy]], Callable[..., st.SearchStrategy]]: # type: ignore[type-arg] + def inner(f): # type: ignore + assert f not in RESOLVERS + RESOLVERS[typ] = f + return f + + return inner + + +# Type-to-strategy resolver functions + + +@resolves(pydantic.JsonWrapper) +def resolve_json(cls): # type: ignore[no-untyped-def] + try: + inner = st.none() if cls.inner_type is None else st.from_type(cls.inner_type) + except Exception: # pragma: no cover + finite = st.floats(allow_infinity=False, allow_nan=False) + inner = st.recursive( + base=st.one_of(st.none(), st.booleans(), st.integers(), finite, st.text()), + extend=lambda x: st.lists(x) | st.dictionaries(st.text(), x), # type: ignore + ) + inner_type = getattr(cls, 'inner_type', None) + return st.builds( + cls.inner_type.json if lenient_issubclass(inner_type, pydantic.BaseModel) else json.dumps, + inner, + ensure_ascii=st.booleans(), + indent=st.none() | st.integers(0, 16), + sort_keys=st.booleans(), + ) + + +@resolves(pydantic.ConstrainedBytes) +def resolve_conbytes(cls): # type: ignore[no-untyped-def] # pragma: no cover + min_size = cls.min_length or 0 + max_size = cls.max_length + if not cls.strip_whitespace: + return st.binary(min_size=min_size, max_size=max_size) + # Fun with regex to ensure we neither start nor end with whitespace + repeats = '{{{},{}}}'.format( + min_size - 2 if min_size > 2 else 0, + max_size - 2 if (max_size or 0) > 2 else '', + ) + if min_size >= 2: + pattern = rf'\W.{repeats}\W' + elif min_size == 1: + pattern = rf'\W(.{repeats}\W)?' + else: + assert min_size == 0 + pattern = rf'(\W(.{repeats}\W)?)?' + return st.from_regex(pattern.encode(), fullmatch=True) + + +@resolves(pydantic.ConstrainedDecimal) +def resolve_condecimal(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt + s = st.decimals(min_value, max_value, allow_nan=False, places=cls.decimal_places) + if cls.lt is not None: + s = s.filter(lambda d: d < cls.lt) + if cls.gt is not None: + s = s.filter(lambda d: cls.gt < d) + return s + + +@resolves(pydantic.ConstrainedFloat) +def resolve_confloat(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + exclude_min = False + exclude_max = False + + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + exclude_min = True + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt + exclude_max = True + + if cls.multiple_of is None: + return st.floats(min_value, max_value, exclude_min=exclude_min, exclude_max=exclude_max, allow_nan=False) + + if min_value is not None: + min_value = math.ceil(min_value / cls.multiple_of) + if exclude_min: + min_value = min_value + 1 + if max_value is not None: + assert max_value >= cls.multiple_of, 'Cannot build model with max value smaller than multiple of' + max_value = math.floor(max_value / cls.multiple_of) + if exclude_max: + max_value = max_value - 1 + + return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of) + + +@resolves(pydantic.ConstrainedInt) +def resolve_conint(cls): # type: ignore[no-untyped-def] + min_value = cls.ge + max_value = cls.le + if cls.gt is not None: + assert min_value is None, 'Set `gt` or `ge`, but not both' + min_value = cls.gt + 1 + if cls.lt is not None: + assert max_value is None, 'Set `lt` or `le`, but not both' + max_value = cls.lt - 1 + + if cls.multiple_of is None or cls.multiple_of == 1: + return st.integers(min_value, max_value) + + # These adjustments and the .map handle integer-valued multiples, while the + # .filter handles trickier cases as for confloat. + if min_value is not None: + min_value = math.ceil(Fraction(min_value) / Fraction(cls.multiple_of)) + if max_value is not None: + max_value = math.floor(Fraction(max_value) / Fraction(cls.multiple_of)) + return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of) + + +@resolves(pydantic.ConstrainedDate) +def resolve_condate(cls): # type: ignore[no-untyped-def] + if cls.ge is not None: + assert cls.gt is None, 'Set `gt` or `ge`, but not both' + min_value = cls.ge + elif cls.gt is not None: + min_value = cls.gt + datetime.timedelta(days=1) + else: + min_value = datetime.date.min + if cls.le is not None: + assert cls.lt is None, 'Set `lt` or `le`, but not both' + max_value = cls.le + elif cls.lt is not None: + max_value = cls.lt - datetime.timedelta(days=1) + else: + max_value = datetime.date.max + return st.dates(min_value, max_value) + + +@resolves(pydantic.ConstrainedStr) +def resolve_constr(cls): # type: ignore[no-untyped-def] # pragma: no cover + min_size = cls.min_length or 0 + max_size = cls.max_length + + if cls.regex is None and not cls.strip_whitespace: + return st.text(min_size=min_size, max_size=max_size) + + if cls.regex is not None: + strategy = st.from_regex(cls.regex) + if cls.strip_whitespace: + strategy = strategy.filter(lambda s: s == s.strip()) + elif cls.strip_whitespace: + repeats = '{{{},{}}}'.format( + min_size - 2 if min_size > 2 else 0, + max_size - 2 if (max_size or 0) > 2 else '', + ) + if min_size >= 2: + strategy = st.from_regex(rf'\W.{repeats}\W') + elif min_size == 1: + strategy = st.from_regex(rf'\W(.{repeats}\W)?') + else: + assert min_size == 0 + strategy = st.from_regex(rf'(\W(.{repeats}\W)?)?') + + if min_size == 0 and max_size is None: + return strategy + elif max_size is None: + return strategy.filter(lambda s: min_size <= len(s)) + return strategy.filter(lambda s: min_size <= len(s) <= max_size) + + +# Finally, register all previously-defined types, and patch in our new function +for typ in list(pydantic.types._DEFINED_TYPES): + _registered(typ) +pydantic.types._registered = _registered +st.register_type_strategy(pydantic.Json, resolve_json) diff --git a/libs/win/pydantic/annotated_types.cp37-win_amd64.pyd b/libs/win/pydantic/annotated_types.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..6c9f0f37171345dece426e9397e0782b27a17337 GIT binary patch literal 67072 zcmd?Sd3;nw`v2Pj8UomKbZCqU+G-eC1ks2RW5zTja9TSW6$F(f2oV?&B6KS#Aeh9l zJ+`BMbvB(DbkuP+ml;P96%tTZS;P&;4VTf5?Sjj&=!|{epE{>Ioe=zfzxQ|VKlkd3 z)H!vko_e-=>ZzxeuDawlPiK$E(}jQOw8yi8RR5Kmzu*5SKhNXob`*teZD?#=NOl2B%G(GiPozcv)?5UTjWq)|_C)c^3w+oIAaCsL$7<$OXM* z!8K#gIO2$InSaGoZ|*jN_m!6wc2#=SR3}||S>LXwnf!iTPci9{T}w!B8&hb~Gma?i zb}#QwUUq2LQj;FxrXO~5-j6cx7tESAQ*E`Uth&|8o8- z_jsCy);R?YjnL}Jr+^{6wIJonht8WnPXaQZ;0?^>zgz+-WXjdfo=ZXT$S^=ObUL8i zNOJ#w{a;H@$bX^rKPx`5DBp_DE-J9%(W1ifR{bYI%l7>xFVADyA6s^)&?@n+=Ph=u zWqU7&qgG>6^bjk}=YHdPO--WslJ7j8RK&8&Vmw&(YY}@)VLGt;UcKz3-ed#LDzxIh z>+~4!*VjOLBkHx%om0oB17m+j*~TV+<4efQvbQDw7I7*!zTt0JZSuUkmXnpZFyAWa z6g$VVuPd|~)6tQZJ-@)RH(Tj$mi@Ms?rPN!%!_rk>`v+GVG;Xzu(9l|$*<3&Zv9Dl zmc2XH!?MTc(?QKvm1pvtx&sU54C;{9Uxx=q{!sB^%7hS#OJ-BAo56)BF zQPkZG@ZufCZ&;;ee}qc^ook9W^1KcV{dYDOH}QO=d2Z^tq35PpPqh}XC~0qR+_A9R zv+n~mwa~J=Htz5@tTVuqc~-oe;Eyr5RfAg6L6D7`(^H63^a zfWo-3Dc0lJPlV^M1j-Kd`H#mFTA{9B8oq|kEkRJO_}R}W2W@x%odkxoX|eH*=w*h? ziLGR&st+h;cedi41+A#Hm}Rd`odpG!{f@yWm&a>U{h!_8G+RWo5qqnZ@EwW7Lo0fL zqy0*YME>Z+blrsm&(x%AKbBv0+Wmya7C8XLL@ zHMYWbGGc!jvENMxE;0--jOqzgD>OMYamu8%QvN!Gh~_Vd#1|EnSJ_MG2lo`+B+fN{ z>Ti&x3)}0fP_!beeqWxyQ8q7ZPcF3VEt!((z=OXQPj{!ldrLa-3+IhWu@ex+{%`eh zOT->YpLkQq#~Yj$j7Xy6tn^l^agYDj^?;5fyeHr1l#f>w4TvOeF-0TxdLbCJ5`i;5 z@_3S!)gDh{)5316<}gO2F12h2=avRxSD(&{UK_SY7eo@iMGp#??^&7!^s7=B;0^=M zJbg)c--jS&4Y2I)*7}Mf*m24>eXfU{*_+6 z71CPnW{?nDhlTA*Y=~80HExWZ1iK!Fdf0WA*p&#(G(7B{4jgk2?{-;@0Bje4O8|h4 zR5!ii8>$@GMGuj@ovx?0`>X1Us(Os7miD_7kfzD`cbg{PTuyc(@RkyrCjU$}^*()! zdatP7n$Bv+`{sU?YbbgMVk3zNJq8&Di+9M7tr)8!STTp*$5i(<)xD9r?X6gaeZK(` zw)gmtDfd)1eh@tmgP9KOz?N7g6-6Wbjr*`9(nK}Jw@9J_aV5qU4NC{QfFEYN;US7x zwu6u9oX%s*&8Eued@C_I-zxR~2cWUzEIWb4U_P(H>Sp+AKLtL(k*n0FL!u`4h#gT6IzqkcnHzoU3!t z?qQV17+(y&??T8zz#pJmrjsD!4Zoxhj|I?^dkr9G^!%&nF{5V?RM1Y1o{bWFyx-51 zgR#zxo_*AYM$e`6UX7lyN~JW)=sDNqdGDi@%;?Eb?TntIFyLsuGkWe6g*l^Vl?|sf zd~Qzu-J&uk0JX{;Fv1L=Sw|BPUNcfT#aXjhkECakGFbb`o=Z(|;M5Z3iux>Cyj5rxDdL6oiI| zT^7(&{ZQ=n){Fw8)78UTR_j%|XnPmZq*HpXo=oZGrgUd16ov0FZ@ym$SRyd+J`(lG zoj<2T?V>}0Itr8O^Xy(;QvPSZR+3Ndvmf$=Ku>$v0W6-;2Mkont)M+)sv7r13&Nlf z>w)10gxZhT+pw{nutlL2gFuCGauU-L%f6*Zp1}HLOZl7TzvLdCA~rS-Ww$V<6J%R! zG?LEL&;WVRmo<_(=9TgExk$Vm)w1%(VP?l$iJA9W%RX67jR=0-9(8>0`hUXS_=Ey{ zoOs=?%HmJ0_=265{SP~EJ_U-?k$BDa%Hj_q@wyF_#e2f_3xfcxdQI|NUzrA!#`g7< zXg9`UMm$bd@i$!TlV^n1Rr3&zPbt8y4BOX`;0XqP`<1vh zGJo$kZMXsKoiRj_W{_OEQeLST?tLNp@+5d8gz8mp%VBiN! zckB(aJU!BZEr2L~BXw9C+w!z1kbdQ=0X?&^#PrNS67nnM{rE0|MahdR1W^XV-}pW? zLFuzUc|3^;MWNM8K*+L1ckN!$&oh##Ojk928a*^zf2GGLP$deqMMe#`1yyU?U#e_* zU!Ty+D(tl(`?X~EAFGX68o5U7#n&H16(28JDk|(}jP69$N$gFW_E>jmfy*xG>V}@#M?x4&#?Edu}-u8Mh}*S_}2g(B;FgY2PAoySPL|GR(`U0uWJE=8K7Jc@9QR9^0(vYV6sa#Nc^{)Q%# z=RNaI^pBy@nWTjAZWwIdh=#&;MNt7vK`+%_dqX=0BW&EI$S6Q#H8oE;s@1zr`C*>w zCkGqL&fvZ$7JxG^{{hXIJTSESKJn#nkjP4*!jXc;wypHeYc z2|9I^{rI|?L)D$PhT}^L_*YmJ$FQyMs?LxO90H$Gh3d{5Lh0MoogI(G-*6w#=%Byh zmpmZ2n*mrQ5b17`=Wb>8d;W7(C>zaNI#3QQGi##@u6zK?+j6CaxMN<^DoH10sI@rzY+W^m&`gND=R*^M$Ug{wH{wGLHf`| z`T$QO@yl^BN7wOhK{`;%*wo&DQz&UijgH7ZNM7=x5)%f6RzD%GkLOQ&u1m=4z%MJR zFMf~m4+7!P>Zeqxls^XwStC7uI*bS`N5dU^(iOv(>|U;1p~8yZR13xr}@Rr#vy?A)3Wr5;8x>G|WP*eS((LaV<}LCxlAaU3hGF@?Vzf@5?m)*#jchG~P*-kEHSe)?VYXy;r00l^@yf)GwYP z3O|n$DZ!iX0v=XL7W7gHlvO^gEXO^*CR)ijQLm^l`VZNVy3Y_b{#IdY#@;)V7hpL>!fyJ0{XSfQ zC%XV~P{a=5SncZs;k;Y+LJ-4{%nF9nl_)#0qSh(EB>B1uV8nSkV2Xe#gSFj?(^dAr z4*Uwjd$HjNAszTh!k}`~Dp+lT2AC&yj8_iJ%TDCA@7_pgVqlUF$q%c9h6CGgA=y#q9+P#s&rHYa3cSN!;MdCAq7(lrqXmL7l7xmQISV3;*$P}kr z4wv6AZ(bu;cZ;}`<6_%Y9muak#|HA7VM&$!btGPkPhp&iZsf$uAmGE4p*A|eP|^Q2 zUQ^dnR8s@!;vEwsAC$BhNLJ@3iQ1ht}&(vEGRR$msv&UKxLSK)v1Vr-Y|Wt>Kt zr@Mb8w#j6>P#%Cysg_JgPV;+C^C?Aybi zTAppZLJ*((I-P#H*dWta@AvWOzMrUf?0x3FNbfhaz8}eZDrS}X{&_Q+aReGL8;R4$ zK4sK58t`145*8oc=YbrVoW=e5n};8Ay~d=3Z=XgKf5issO`={puuYYI;09_FfjX19 zl1z7?=-FKi8ot{^FFR6cgww49Q>4jcd(If$Y5ppmHca(5KA}gPQsb#J*L{$8+G?P! zrw1A)-Kk;jr(~nFp9-lyWK(1+MMA5m@|9lEGLHf|6UvU`udh?uMEE`N*MH+5 zpcQ2_i+3wO$%(%{{uAP_e*`#&WH%9j_QubNzxDxMXa%?^ z{u-L7d3O8wYbhU984vLPPW)9|U|<-l^&uj$<1V~N@pUI2YkxvWb^%WLHjrg#x4*IC zRlD%6R)3($WeacH?brF+WPicm4Vr?Ju3!$>Iunlu!mN07;Oy-{iq4!Mnvd?ZJP#r1 zKyNXU;l}okyiv?yPITS`t8`OzApOAK&?stiXZl^D-I625AR}+`*c!+z$g;7|4mPm>seMpP;T`|5DGo>8iH&Y)I+E* zt}fmYDIFe#WoFfg=iOj!=2JX3K$R2yV%11tpul02G^<8}`A?#UF3i^jv)O1!Hzs0@ z|L!C~C-cJbuDL;^bl_q@I*il|r0hOGBo>SeJ@dW*+{GUW0GS}TTi;XB1J;?wfI{mG zqO8V17!`WvQ$ZMIP+(E4)a$3E=wu%HKoRR2I8*B@YZReM2cBl))jE2V_07QeN-)wg zCU!Y$uwoYsQ_n2|)x8AXR^!&e!GEdPP8DNPW`D&R01iS;`^k#dawe@%^^4GurC?!| zjLwh!5Ib4pxT$|)SRt5$uhEy-F`z1ItaOtVudGRxn3vMgIJ1qW)Wmi8ifxMYVSEdv zU!(X6H$u};tZ_=#CDnoU*uLp>1=p*X%A<)W|KgwIWxcC@ab8<~2bGrXi|LbHwhlQQ zJfgfpbU70`JV;-jUK4>k7?Cm&<*Vz__zMBF*a753lxvI#cEXvh?+b$QK{o3#-mi-q zX40E)DE>BebUM&1xJ;wQD4<+)l4?-*Ta`KuzcOsUV)XM%+M-k+85w`$wI(t%h90}E z$uO#czb3IYKWAN2UXpIpCvx(Yvj{kRxv&mb?xt25+uP2bL+bT}YyM zPsfq2u-z5C7q+?a(|BxGI&g`=#4ESEo<7ZrH z_-eF{ru+@l`DCVA{>F>-XlH_JEobvekKJ(t@KJM!^2A0Uk3TFUKjFPVohA|Zf$2N= zi0>)eE;}CRG`V^-#Y4}G6hC%)^~IaFOBE_DCTGd)RB&tZW6ZnTaadL>d!L|XV!`Op zGZilGzoK@9z1X6QxpTiygw6O&FXQ6Dd*hMD)(gw4Efm})xnzMAm&&ihu-1`!UG&^+L-&&@xJEh24Ec&5d6M-kup-k7l`+BI;V$o#|Ag60L z8jh%IfAx+an6ABC$i(}V7@S%ci&_L!tbf=DeOuBRtSU502~3BGEHkmd3Oy5(Wq1(~?nLcRl*<`E-(;>}%3Y&! ze{h;&om&&NBt+&6_>X0ZE>h7Yrs(4BupXL|f5y5vleMxqs53hMGuHawYS}py1#Ljie{I%S}x%gvVE;>&lkY&3>uU4c!PNy z$7AYR4IvlmsgAkW#fuTX=3={eK*&>X6o(M~Afvf9m-*7a=Pr+K->3qlN7unU zT;CZ{!|20|P}ErP(zUq@0IuD8-}&OsDb4;gcURi$>A?X_0lP6~7K1wTa!N|2^v)*GG>=O*ZLEyzizoxZa^>v*`$IrWlm7Z&v!g zaxl)B+v?G>!vv|{{yDC`<%jzBnms0#7JR>Bv>+PCyDJl`Ql2VuS#Wke{e|{{Is#B zAfpsv%?2fuFNH{Vgpz)h;q!RVHmDVv(H1ji_LfE2DI-}M!L`ei3|RYStN@k31+Qjp z@9!51QE$1lMWbQFcocz|uVhO!IwgLXDKUr=xOA^Vp&3nqx02^{V4_nnmMNI$fO@4B z)EQ2R--$VD>>V>&1$t5=q<|c_`7WY3I-+@b9ecKB`jpN5yoPT-=9``;@I3i7=eb%L zmYm9RJ+Z{a2AmKc6aWSk@6fg{@BqDj3sk}T8)!F;U*QH1xT5Hgz8v271V-ZQJp75P4Sj|$V($9_ZhNl*=2P7q`g+nhn)Pp@P|!Er zMO!otG%cHLRNk2TEL-%LiXwVQd)tA3Ooo5;4$MbHsvX!sr3k1Sl{!OBqaHPUINU%R zE1Cj54NYSuv$0u~8Y`7zKN#Z#^#{Nvsgz3=jOmsRtc?kt_e3?dcjr+}dv~Tcum?b$ zZS4w02r@5i4K6E+7dKHQ-G2uF7>~3^#n{-Ebl~Tb6x;UFfxn9|JMijC$tZ2rf0K?4 zVZSD8g5EXcvCYAM*P8l|^J2v?BqN7^jBChCeFpP@7tD>s5&vc9pMr}5v1IH?}g9x15?cbR~o6~_J zQ`2#_jf<8DG#VV(PdYOTHF|QyZle9k<`co8{-7t=6>YaQSF(P7)9JuY9Su!|DkNgI z@gTM8#}^l`MBubcGkFIzm^#w6i=4=-14 z7aK^eP~!Yf=|BYV@;=z&X_+lKk-=+2{wKQRv%zCGZLR{Cr=cl&?ufkD#a6mGy~e^( z8H{JKugfBqf4sk|6G#i@v=Hp|MNp68jr1dNBktY8t9nP{V}J>*I+cN|CBH9u)7JQ( zIHYiC%dn;UyI-?{4G9}HSlv`nRAHIjrh|80?mwmig7g;Y+&t3%MDNZ^zZn|qQOTa& z!U}t9s#{{@_A|R*!`X!9&`pSNf0sosn#wCT(3mmw+9^wX#k+?{ScLu?6y??I4~IFL z+Olz{TG;mD9W3J~VwXazh6&5dj)IUKn4`a9bXYf&z~kQ7i-w+9&(NyjDs^V2)Geme zhfb;W4%I4kno9Md6p)6|L6TbP0Z>D0kr68L)sbY|-r-_f!nf`zQ|P>vCUcm>%Q$bz zMNakFRy;zg{1kyrd~eE-rYZftQ~HBe zye6vjr3SD{uAyW)@QhRJ*;b&FRqRxXF-kqC+KIsL1=RM{=}oXthb>Gx!JtNky9s7V z*;UUlv1o`(R*8PI^>R$P$N$8rBYKP~wCtYIlgme3cVsj_9aus2)G*7wka#h>SJRtx zCZZ==n+}Wvom3zlxU!Dary_i2f4;E9-LXgQv*P(CvGZ{kSui+4RNP8&4Hy1~81Gj7 z+5uL{b^BsBnEsLuT(8e@@4l@hvY*uydP<{PIxyY&^l9ePa6Xlky}B8z!`1`uzj$C= zI_(7|zPWHI9mva4KSR`aHyHGtqY^t^!Cavay9+f_rcW_EJgd(L?Kof#1WG2q|CJd| zUa2b2QiXO-qJ(7+$v2I2wxz>R(>%iy)8dKLd0H(b0ujc)h)&I5T806f_)#;og;vcI z{7Hfz@An8f$sH`GIzo)<(@1?v^gHD5B*OSlREAeK^;LdJS#NaRikJ20bF3eV9aXtf zujFORQ(Rcv4=1CfZ0A!dRo23118aodOTFD)Sa}Yt zA`+>eTq4(JkMj#7EMBPAUdVPnO;UX7ghEdMPgv_^k<`FlB%VoymgC z5usJri_A-fg1sU6!NttHhM#LVYyy!evngcW}92lPobivyu-DRpE?o{6uKH+(%h~p_DOOJbeEKlXT#6syZ@> ztJGh63zuSzahXF)PjxkF`=2YaAbTb1I)zrSElZsy6A^3hm4eaqs;QyjJ&xrqaU>`_3;4d=Zn||B|{39ybtwZon zi0gpAu|4?5RA-U{{`M^RyXbX+$7aF*12&}t6>Y(d*uQBUbCPIyOx^bq+*e9ETjJt$ZZBpg4O*`I~ z*U6>CWV~iH{XD666M3f3_`c&Q`R<@xHc)??>i@#9u3c+^Mpb3wPhxPpHg~AbKMIr^ zsG*Qq7Vv!smH6EtXw;Sk5Y+Z%6@5fS+Y!@N58P;fYKCBDgyapadPmh~3p?9;<8v-` zITm3ad5%Tc^*m46{=GwQ#v=GmqDb=NfdY|X8-PC&;KL1GZA=ps9W9vX`_J02iaH;w z&XtlA=jqmFx>Gkp_ElhR$o3ktKT**Yeu}oGlbWXsHNcZWX<_1MqE`>~i(4%hh6r_^zQ<->P~a(~{SMkoG!rDtQi9|EMQ%)#%baCf`ID zM1S(m0Ro&67l1qW0`NH7-_!Py_tC2BjQYd#KJ(2mG5^@D~{H&(yX7e~5~Tgm&PW2DO)>_fPQXxIY%kERc ziUw!lx@k?WhomY=-6X!Fm$M92@CWx^uBvO+A#{C{2t8oC%Y;^e1W=u;77(4Q!@sK# z;jx}TXisV20gy0a!dvZrncB$6gL-3B?}w&dJA=0^`DV4d)4yO91+dy-6|OhjZ8Ipa z{}Q;;wtwg4!uN$%ja8L3s$zS;Hrz8Tf0r(2SiYNwR+jfuzG3+Y$TywiBp%SF6{fD+ zIf`k`=^XcFK*oKy3R1k^Zw6jZQ>aV7r(M-e&?=DrXG4L@g z0lC|n7`~Y_Ei%n(kJGiQB`S6#l{l6Cp~N+`I7xMTN=UZ%w-jN9yXz0a%G>fRZ$u-8 zC4YKyJ2WS&(yKl6eZ@ndz>>C(z~dpbaUKE>0>a?z8{4EFwF+!<@RnU|(E%dx_BYP# zr1_oMlGSoC&#N5bE>fW%Q>c|uhlN&Mquh1E!1nGF+FJTA@s-hxk$puod9B@TIIl+b z)8V4&ZkI|Lw(l8J*X?engPzmf9>{=<`##oVyzDC==XSTd;iBno+M%WH_KiWt>2B|u zyujhK!Pug+UxBq#4Cx##7*ZI6I2;Tlcb&*M5~F!Q*YX~hL7~KV4R8!qdxUf=RUh$c zsJazC8>$*5Gu!u}%4N1-Po-sts%<9Q7?C19#>*a1l4C@k7YAfS4tJ?4%Au;WL)FQ& zk)i6-$yusKMzd61FRCiK0jj;h@Geq=3E#612!{8@r+G6fcH&7qIXwZ(u}lDTvMb`~ zSC}mmu70&@H#$9<`p)3}HAorlzSO};+Krkt3eb=P`OCQ_%DD~V)%FRkx?M%1DoXsu zu+eDLW)RkW58iKx%!v2S!H}{PuPwJCMA-ciXrvZn!`~J?wr_^1?kd%LlZ2yecy$J9 z+!q6CBwqF)Ky=LNSdLjyVD&`x0Cm>kD|spzlOG$E{4xmek?Fu7iIMgXgUryXyTKL` z3tOtiX4LOGam(JPJ%E4L2d7BO$$U-+mViU+u*bJwi`W3)+F1;m9!b+?6x?R`*^{bt zS@9DlEmyq@y+CU}#Cqq6KTa=v1U49bJzD(8>T5s0_$2q_ZQ3vv_!S;_Kh4y2E%0M- z!Lh){WI)D!69p;WZ;Q$ksm>e#4@ipA*B{}o$vuf0b~qMzkjV>dBwpeW#CacJ8w13Q zpYHc(k<-wHst1tsJ2mK#vl}!FIa5sQS#PUaREV692)P(?-sVB_H+5Zd-n&$J^0ulo zAmhGYi?VpXUhT*kEjfvt%fw>HdBBk1kaMod^Zx1*ebRukK&?3g%9f+SfC1$$8f&$a zM}}7EqLG&SI#aMcg@IXA&kQL2sh7i-r&VhYd^tcpyhn)$;-$Y+L*Ba-sxlq;iov-p zqbkn}#$UzT?;j&B0E9C}41*i2lR#e%DjNmno&!-y2NqJWmDV#tt6s{sXnQ{}k}y5^ zF&>Th9u=}#Z7jQ&S0lawdXNitj0&*MY3jP&_jto@b>G`FAmhHtdW@G%F#ua#u%Ez1 zBfg)B;dIImMZT;WF4zqw&-=y%WvP!ZRA^9ryj!wYKSzA8UaXIZo6SZH6razL2L@%Y ziL$Tq_}Jc{1_nPVF0dbJ1lzCj*l4QU&Av?pBi1Q?C#orP^0Av`e)&8Gbmbt>GSy z977n?p(P#ouL&7S-WDbQkfm*}!rt!;QihUIJQ_+m36-pr`+Y^ep=2cN7A0jR;=Aoz zW9quQ-^yss>7;+kfQWBl7)2 zaXlcMp?DT-GegIAm;g2!IslXsp!Fdc4;1$+mHWi-Bk=0qGC&naXB=}N z=7(1Oi&9*dBENBj2+W48%$_{M&(Nw}Dm2Y~c~dVJxi7EiCA{iL!=@+n(sR`j=H-vP zpzhHtNZfVOOf*a*^`z91p;e1jfjGiKQsc-`VH6c~>_1l8>Y=1gawj(r4ADsC zaJ}@RHvX-kTxRBGLpqrj%cQ2BXr)zO7DTX~XduE;R>F4+0~axq5a(UaA1DlcM^aEb zf6mnQBWu04t19weY_P?`2i||`xOBkiA$o?}>1!EBg+yQ^kW7TX`&d1AcNqO~mP^^8 zpc@Xff~xy7w4&==K3=5^tC7TA@-s;6lx}`l>p~&lC$wm!mz4mDx>Qe=O{fUrG( zSlE7F8#;3?QESg{?e0`RF}vubDwh4wQqk4;X{;yv!B9W0O&%{EGA)-73k^nGkJc%OafvTs~kE)gx?K+6T`z{`AXrDYv&h8Dbc zdhJ=N>h{_Tku1IT6Ei^3F)pOR4wMVM%AwZLuL9i$R;c|tPS;@RIF!%Ee$FY;0G)(u z;-H+hM1&R8CV-$h{8y0}-6?q~d)8YX7e`9Hr&FAR5$x-<&n~c=>vtu}#H-kFXD8>LtJK?5Gx#^ngoxE)%egnkl{+jP#erxP#W@ho z22(BKZ?-rW&B29anU2?BUD*lVCdANwtTY#@X*pt|_9)F}w-y^#CeZ7KnO@gwLtK4m z-A}3gs^9D>FF-5m@{#8oDM-1PXz3sX&?sKT|bDx8TbY~-s{VS!QWr+IT!;SH(6sjBL#f+f)>0`D5@g(_IIE&4`u zU?@c&qUZdg?gWG=h0wo5mPZ*}UXLB+?pj3ZBa(BtjY9aB%RWtJQ<%4cVmha9M82mv zKiY@=@X(W7D@n8#&C!OCs{HHYHave8fi;R3*;YK)QimLbAOoq3gmLilp|=<%(9~)HUJ6h1=7C{Sm2vzd=F3 z9Qz2vFtw4QncDq~H@_xQk{`Xw2%^53O&CqF&%#D3Z?_%_^*8A7fhsJp>>lCLjnQ$G zDX>aH1+f#t_KS>XRhY+p@~YB-La|7W=#Qvmn!-UTGMhi@*V4j{*h*~|VcS!9@K&pN zf9G(#%Qp9BIx>+;PQGRTKsDxu?YASPd!k`;sX#s`_#1AA=yqs~%R%FIsiDxorOcvH z!)6kWO}!m^5{nw;u&l7KU$nFnZiU0J#-FG>848#`Lb&0T)3wHv!H#Hg(iDTfX%Q!0$oFRrmT zU&+4-{F~YSSct-I?AB^~NtHBxga*_Dw&JOqXwN*1$n*d`I$KChuWd!1C*hJ~|630) zV+^`+d~AkVXnyl$FA7DK+tdl{y>E>?uqyog<$A1Mv|MxamS;VwQ$aa02he^i}K z$-c)4Mhti3z5+GCeM7y&^(zZ`_C$N6u4nUJwVsUv{Zzogk1KeGNg~`NirevOl{w$a ziuGI&a%@L*kd_kL9i|k2Io`poww!{>Y!FF>GQ}>!Qf|eKt6x-9?TL*QDwA0_BbQ-) zb140?ssdgEQKq(pc`Dw^@GvcFPJe<`GPh9kQFA3*dohRO$E*-FsguI-vr@)`@{(t^ zlU`;4A`$4xNTZ7gjsJzQzng5QGfa>U)X4CoQ@E4n22lD$<|u{H19yDOjQ?gNXrlz2 z#jLdVhwSgFq}h1NhpAn2bB4d6JIH0c+AN9ARdBKo_VQgIFHTSwIUEMV%$qUSRz;#N zk3FS+)4`K}#V#Y+CG^16V?>&FCUz@a|6Lx(k=|lR(^;!TU=#z6Ihdr)M-Njl6)`aW zZiH^Y)6z*+{Q$FfwHk43KuAEHWuOKI0l;-U1mX&T=$<^)v>u(85$4zoSo9A50ntw1^4h#QkC>+5_+w+el0OHRFnexir9L3dIJ^DsJ=S zg!?8*!}i`gngreQH)xzR#-{jL7->P5z649k#L24QB*> zmMj16-t0i>Z>0MHJ98MSbNWEr<#aJ^iqw=W9s zZ@AwS^ga0?z)G9_4R`X+0;A@S+M{?`*sd;MbXjs_I8k0`_On`MFif+5wh8e6SoArz z;GqXJS8EddDmK|U{+=1kL4h?CN$ri;`B_US&xZ~$Wv%}hXjtkIS4x`^E9&++2j!N7 zv;WSuDX+PRkWPO$VS)mDye`q(9WwTCV@m4hrW==fFRsMYjDV(c7>%ZB2i|`wfZ5vJ zGc!Dcg7Lz$IAnb=A!S%-Z_Q;GAu;H0%jAdA|PS=Gu@)sSf!&C*ypTs)x}vf2ttQ@ zv~&Y?Q~BBkIa;KfctkX2G|(aSaAkKOeT@CN5&Jxp@}1CL!)t?cgD4`z zfi$J*PFI_@dg?-`cuzDXR%pCc6E#U4!fAd8s@?6}$IL-~(*;LT#~kGU8_0+dayiIf z&8hkc>A)hs3h4wNvE$Vm?(Is-EOs7ayIa1125CYhF<+K!9yMa)=_*{Gat?Q1+^Wyc zL4HQo$mRntaPv1dQby5^1Xlzk#_dY}W}nQeztK6yvUnGV`jahklY!p}p2W~k2mazy zT48HDH+Q)G){ymI<-%%%Y}M__Td_mHyXiuxh()CKelz@Xst?m(F5Pcxk^j?rx}OC^ z>YH2LIONYH+4%tgmJywMo#U~j^Z>(e{IMul(N6{7N4K!gVjV7KB zPN7vgl$1Iwb6piZKA#F4w8=OznZY76z-9a;9lnWIpyMsKfSb78a*FAMzVm1t#os08 zNE-jsv*>s~#zNmpgXSo6fF>yMvrLIARl)+)j7fQ8WD>VQMgzs=KXuC(5yHYP5 zSfEO+N12Z>nbQOShGre=j16~3@OzwlUF)B#&N@<)iD{btruYd?G`|m#6JLzj^N4iK z<2v>x{3h9&Telz?3ooX$(BJSj&tXD^jB8b;>lURfyQ~oMbw^~iiV*3f_JamuT2 zVh=n9UK^djvdgkTq>Z6v`=V5eL3wQ^KARoyeJ(NEASUHCl&EhG zq;fdj9Iju3AU6AN`7!VKT?MB14KIfTyKD&woET!IpSSAQ!LZN#w~R%D9Y-63oc&1# zoTp6i*0hj%yUld#h4PDZJa2??ymT+bY#kc>SoiPO@JkHlvi(YXpZ%5Yk=YUZfd-wc z%7lY#u`FE@Wgqh!*o?Z(uH5?X9M$yzA(ZtOrym+RfQ6Gue6}9XP>s5`xB5 z)o%(6)1BC8ije3?^)=8`A7uWy4wq^K^Xs#Bp4qUpd7)dG)Gotf85IfMI; z+(G>-8gsc?N`rk3PTL(k^sH$g7x8DT0FuU6yOidAUl=BX(E!S2^EG``fdlxF@z>?7 zCwwp93US`X(6U=7_RR6;%%DWDU_5q&Sv>MI<&5a-aG>(*pE4bMQFnNA2at4&g< z80AwYYr*5f)`*Dt^z}x73znPTnt3PnD|nHSPY5IZy67#TyVJK=i^T!tB+_O9jE}PP ztzU-6-d_uA4bk5JGWZ+T>z#sa*i2_)EduC`JMd@&wWeMtD}KJE2?aNsY8n{ks7aV< zVEfAia`7AC;>U=r!}1V)l{6$iYJ@Oj&}>D)%qCn(JMA~qyyo?}SEwt(79L@xJB8!b zz-aI78pRnPeF_QZMBo(TThi_?N4Q}r+RWa2kWnkRS8f;+DmSY3-?UFzwO{^y?Kexx ziNHTHe&!MXP5U)41YUdhQw~(xYJ67S!(4tM;(aO@ABB(Cj*-LK%2G78(nC-r5r`S2 zd`D*#aq;Q7ylKPmU`)LtN`=%GI6usxoJJ4Ad55F$({TVp)eb*x@39VD=-%NxwIz@H zEzZYad;bF-M&&A5@5?Qdga64)RbNrLbSKM>D`*yu-)3TG2Sdp!4ct$KDEQ}SQkR%! zT}KJ4MBq8RXQQ3-Ps`;*Mm@6qZJBAv>2eb-d)acDhyT&ALUr;cqjW_5u0g#isICQ z@0>sU`{_3g{Xa&3mMz8#Wa$TogVG;83vs?eiRfu<#2Hky&leG65`l3Ua_&DhmmE|k z)k&d;xa>V_Q}W+vvZDf-{%2;JZWm;6Dke;Gx?kM*k?n971Pwe+JUDZIh&b^9+9 zGGAEPYg=b#=F7}>U+hc=?QO;XWS-mBv@;5<@8-GP_l%_U-8{GbeqEjXsk1a$7-s*g z$i2?emNY;C1JLZn z1u`MsAcN;@<_nGn^s?+%-0Z`gY$N`h>~E#Qwj=wT><^r5Blw)`El#$PdQSEePPP$w zPWEq|Y$NZS?3CD( zlXJz@e*T&9yxAMF6X%<7T!liaELDf<-}8sK;ltnXSGdAcCx64;&Qo6W!chG;U3Bu+ zcL>^mUAoK66fm=g43m%B@-!u@9O-XN2VOggLRga}5q5Kg>-U?x#V;&uG5sYG7(nIJ z39ZE+a*Lm*;$+9V6z@pQpq1Bfpjj~iKi`>YHvP5piT@AN*hr_s^S>>yq6~|K0aL*xcdT^Bnqe;uGve z`-qrUM9T=i{y!DLyP4{~VT(SW5H|Gr8*YYT?YEd;5U)2qDn4p}6~7cKF{(z!jNbtv znxxB>;@9Cq;OEI{R+M*sIgb|qW@77cv)HPE6e-WonVo|hwK+a&0Y1l48lX^J#u;(F zka+!a^T7;N>A*LJ{S;+?HcMZE3?Dtu5?ixUYye-H+|Nn=mVtdYeie zZiai;fY>eH?E)=o1rW)3{mc#t4diFT{=_#nXYjk56xUpZIb51N zpq)_ssSbvn3^?M%H;}0@KM+UoAl3=Q(t+8AZV7FE4L^)=ZzJm6`S)Hdn;^y zC3Hz)MPfo;B}+@M!`$MT+ueFRF#pOoi3atdPA^dNsoS zG99?dFmxBh7iv$;hU66XO4+;Ncz%W56xzEp)tMiGk?}znXKSllv}l0Q!Ai=6=%9V1 z4Xi*8w*#GxVr)r%OYblB{tMYGKFa!q)w!vnq~iH}U63&Gd7`h2it1YGI6cA*jCYr+sevkKl$s1nIlFxWT) zZGEl(#Tu_+Faqte_w7{YkbVrYY#!ZRSpG-7o_%>|Jf+`<=6r&~E`NjZaN^#xDYxEx zCV$H*x!yaJCk-a?I_z_y8DZ@q{m5kdu67V?OBSFmk!ib2pI^+Lo^^=2B)UGI-In$t z>#ca-_3~W(jW?=UCzhXDY`-j>)+#8LJJ8Qz5*rC8+K^{}Lo57Pv@mdqc%{pb3 zy6SV2yf%J|XOqJ0%hZ$@OL%%hLC7XP{l=0bGVy6`<|@s`re9jb4?FHqc)B?gAGb|> zI?Q@>Z*{KEBc;9qTBaDcS*E^1CHGR+dXk9Bnr)L;Y>fUd^* z6NXE|LLyS)iN56q;K5i3tXZ48KiAL>Ahi$eky7t%C`*XeRWf7{(34{T&EQ(PDb~ZZ zDiaO+vhjO=;$bjtjp6f?H)0Oaj0)TQwjhSh&(;RYv@TU#mBT|+!Ck6Yf}EQQZ__%( z$n5$7>lCOLH+`ChP5i%Ac1IcfrGBM>EdvY*&6%Ph6ZOr$vEgQ?ut#MbiGz=hF?xI3`UiSbMmtC{DH`I7i|{T z<J#Ct7O4; z6Y)R$(sB$@YDiWCyF@FP0vF^FF}Lwp`F1D(&dLH9ZkmXIVVh|>254f0;Sv`IlGz^g zfY~1t8`B|)rcfBK-PyDdCe9vq7h~CRlOc$#a}TG5 zS@XViydhm0b1ml(ctf~#DpAda7kFXUAeSk))nj;8HGZK(4+x~=pXunx0qe-jX7>p? zGGHL>z@5{cqqYolP@f>~%MWPB&iK)-a&6~NssA}w)!NIJO2+m-&@_lWMK*RUUHOjV z@K?{l5j{dY;dfz}DVhn?RmW;xBK>l=_prBvB^yH({xsE0u}xHKQ_M{1*@qx-9DM-h zc|lzI=&=|wdVW)s9$(GB(S>?pwgMcU<0HwtQBzz^*W%o-j#C%ERT+#!>weO}Ltc73 z1JijUa6^iloGi53Ei<0JplCpR%m~rO^zH&+oJ}hxum$)wIw(&nOIns|t^5*A`w7g@ zBXy^#17u8Fx$wJoqLP3wx;89%jJ7@l6hyW$MklX#XOgh&2bo!GX3FVKFJ0EQX&+uh zi-N{XEPYC3(*jbv3f0`tT!VF^W5^;7U?!1Fb=ULmh@f07FcA=Llg2L_!AwKW(|Z6} zx6H5*W^4P!-PwW<67OdceeaR4V8K?KiY0{0X8RkvuU|EDaTB!JntW#~ zI~;I|^vcPXL(6Y^L@2_&WE9q=m_-p^*qVHDyJqb}GtMsf6A-8#mMIQ_g3Bfi=Oh7t~nx1Uh&%U3w-qc4mi;Ye%!Ej=HsW z9pGu9m?jF9m4S&L6>{gM*}{zBIddtZdP7Un_7|){myFDh@f+itlIDb{6A&G-xknpAhp}cm$-U&V zpOviCKsUzn8`V3I(e!y^xBM0RB&_n#x*Hv}%wRHJ|AOwOj^O!k$+xS+QJetshe@j$|y=`T5$q z)h^!ExX1Jubza|F8Oc^azl69g*+ZmYSWoR>i>~Akf@3lW-g;EVaJY7e`&?DTiM(Aw z1CNU&o3$+8Jk!*nR_U5(#424M9h0+QzW9jj4uJBg&pG6mJej?t$VS^YUA^n=p2Z3L zh(_>K8{VOpbAH0K;q7h`nPlU!d;@M~Y|qXOBnIM7nKOFBTnSVu!gev6bWu)XQv0J) z`8SU=lzImXeME6T@0ds(K+=IUH&8{GWqMH^d+;R~j6xt`aM)r*bXan(xnk1a@UBE+ zLY>JIr9244IseqE^XFBY5h4`Jhzb(m!Utv#+kb?Csy42vz zX&d&xv-f{M9;`681iWH)IVNYDAuidnmVDO`!Ny(-G&^jHdP5vE)a)AB4wl>6SYUE_W6!rK-j&oPd1x( zHLgD}caPqajtkYUR}&=Bn5Bzzvzla6XUVaxxzWj2~&C+89UJz-hw=C=c4uRc-BEJqNhhXvh+EQNe!w-{6MFp z>fnaXAuUkAtXu{)nC$O*PHwSQl=PYSP3tBfmdo}rcF=s=-&~=8PaG*rp85$2kMq5*T+CkQbURQt@96?9Rul7iMUa03_-7DpZwWe_igaWiSoh3eh$U#dfV&{`WkLHd#`*~N3GKf) zoq{h&u`M*Cr>fI_IT&AA+|8~wFa)=*wL11*vt=UD>=!y*UG}>2%zEoD101k^TAkgq z2oHi`=JY=5d^~){C&qm0@lv9COb*XC<{`T-dp2b>zO>y^X8e<3^-meFH0!_RbKuL1 zMN{S05$D7LWCF?D&ugHw=Z`~a_RN}yyWOSx2Vlc@GZu#yNT=v@;2c4mM|6#dXHCj!9c~X2#>$TW+W(PUDcnai!g=?Jl zj3yqey_22x&d#(K|K9dwf6P>j+3NxnLcZiUHhj}{(%N3`z2FCiEj(FzGSfe!#_7N{ zs2EY(UGvp%b$Dzizy6FMu^XB>H^POhShK9{|KP#>+M=cS!J@aXT`LtjzK(BOa%`pQI zwaT1NVw)Fw=7;s7gklqMaxO~|3jLl5vgrDfAw_eZ*O-*PD&^VIeup_b0N z&>)_{e*`iS*uzXN^Xo2X$mis8^!Bj!3tOxaIrA^gK7fqIDD+tByAd9RSQ4_g6U*cM zH$W!<%hT5dCXm?_>bbq=cKTep|8bOUtK%=ckP{y)^@YyEFDkH_n+q=A+ni3v^440- z8w*-=*O0Tn4&5waCqQ(7{`9H;CNG*3%}p-pN*DJg&m-|a;dBDFe5fm0VYTlZl4P$#jQt##oa7V_@_mr={V07;GAml| zoej`V04+Zyee>qzj&3|Pq+@3#3-cwromTw_k2>Xs@qQ3uqzt0+x`;XVh6F+$=tVh;Rp#)M!bUS1ey6G+_3=Uk8``sFP3YiITsY%%wj1RkpO zc&^u#AfM4hVr060U*0v9*oy-7Tns@EdeksOd~fbsLN)E33OT8d|CJhWO@~LeCYR%^ zwSM|}?k6A0=X|P9=fzF~=qXgwubXX6egVrB1iGs^xy}rM-k+;;(NTM4%A^B*$zt)x zTc7S6E6=t#G^a&Hp1hNsnz8Pz>0%j&rFv(|vOk=z)+k06xJzH4wZy8QUNiy>bPs~osUtWb=K2=`S7g@dWdKV%h%LxH{!~k^uTOVc z^q7RFShd^df{cCo+3g_Q7$N+gKIOi5hWv6cPo-;PQLDbTkXEYL5~-^~Ee=jw@yY_` zTfEs#*2#JaBITE#pUR~A^s&DK+z{-x7EI-Zx})}{zXHpBhD zhtx#m?|Z?R7}L;q=;^KNYA?Xn#0)B=`@hNuY)Znn znD>_Nu&Nx7UdA&coSOnK`4OmC_4!4~51ofjw8ai`wUvB?N-+A^TFdUzpTI0=S@|ii zrG>xZrrnnRF`os%*k`9WHviGQ)L!vab>};EyCt8%9;J>*UdlUW^G+wb%PCE%Zpj2M zkLH;MRR`l=hMHmg?{eT}U|mnShNhS*9hRKuymU*>;vw}(PW|L~a*`MD$IW!*i?X-b zt7UF++O0m?l6=7{5@UVpzs-vsYD|Y^6OH85!rP1&QmYAO<-)i?ZGHNdHPkh<{=@)b zQ%b^TU9LRFy=TcwZgQY|lYciwweacC`4{)S<$fNV{_lUn+nLIZ8_kJgf5qCHykFG< zf!#K8SJ#X=Euo@A_OEkfYq=f zu)L42$pCdBU0DB7?_TpB9SWC=FZa*&)`|i38`59-ulHU>0d(BF=rpb0p)o; zaF;tbM@pOKVGP#{NWDWOiUCTR(!2iJBF|zy^*}GXl6Ep`TDaEX(ao^j9ZPsav*=g#{THe>D0B%h@dPx=08eX- ziOSs^lh?DQM!#@dE2n?)i25A@>+=&<=j19gj^9(XU3e{fPCUz;m2~DRiGFV!FI>xB zCeiN3&Y~X^cq*38xf14`c`eQuOLx#>QFfk_==X?I_n~%m_c4%lRCf&utoj9-Ar}e( zc7$*Y@xBVXSzF2M9ie5r=R|8~hn9UgYff#r!rpA}W}87`R9J0}+J zQdHUVMc{O?_rv&n*1!*mU24_)J|6|Y`tLp~?dN*E*Ld+P^?1sfy5@PJ1IlW;cJf4z zEvr*Y}RkctB89$@tEpH-u|hK2;C@E zk+8lk-M#3CLK{RXY&kGpGHCc$zTbU*kIJ6S)LloF^_`o}7^py`&X@mAZLiO+OWA{yH zkEct-elhxk5P^@XY1Li!?u>MPkxhe<<6jIfZ_f+I{_D6gApCGN@ zwL8*zL!_8yHeT9t@t4?${(}uFSRDP=I)6*=a&p@K&H3K{FeF#5O|C(f6K<|5myMg$ z@-?d}{P<%wJD7ZroZHODwXGkV!lU+Q3NveGNg~gtYhY7!y&DIncYeXXGrO|R9)~Yh zRg-LBEkKZl(T8cm)F|G#Yy^UC^55BPHZUil>2fz+>878!>8oyZT95 z>1A&Ep__i^rkmU}3ul+})ZnJ~xal9=bfuf_aMKUnbf24ceb;0DJK9Z8ansRmdWoAx z-Sh@Gy~9nHyXguyUFW8+x@po)_qgdH?>X)Db<<&PI?_!ix@n!8E_Ksix#^$X^aVHl z&`tNaX^;0E_{X{FFgG3TrWd*Cxx1WlOWgNc+;q8{KJ2DVZo1V?zjV`HA2{`k-1HPT zJ=;wuyJ^%-Z*tRn+;oMTzTl?sx#`z#+U1`P{62141a2-$W4FjrVHHkW;gwf zoBq{J*SYCG+;q2_c6IwjUpGC~O-H-wMQ$2((;An5XSnGAH|_1FUEDPJj>r7B)lFBq z>BDaN3pc&dO=r95L^mDnrUTrxx0`=y~9m!aML+%TJ5H1x@n!OF9Y0n2dc;O@}E3Q^LWXmOCNUL zm$yqZcmOoVn4d(c0-#q6_P4 z=MSCc)HvvO{TMC(&6+=DcI}L*(-xX))2Gjx7B%^qdPCd8JxGOV^J-~)%9V4c*Up}D z`MkMT{!iikHx&7>L=gz4e%f}Of zb1(aG?X+lc=G6JYIdg+kqtSV@E{jELgD3PKT0Ct235Fe>49Hp0TE)ga2R1)v>Xo(A zqp`Z#wW{&Iz`ISwgVvierEcMZDf6ezt*f0fKRPcqEjne&fk5XTW*F`0?}V=GF2yIBn+CIWua5(|MVO;su8UqcdmC526FH*|ouGQ|AQd&Y8V1 zcv)?5_S~t{DKKkJbS`I)# zYj&-2J)XJqX3dy2CzD?{b=nnEXXsPi)OodY95n_`-PGtzr@Ttek6q^AH)Ve9)Opip z(#*7}(OGlnNT~c*M?aTT0UJA)y@gdy0UKWJjfmzyr6c-tU0ryv!>3T zHQ&Gr!UcL`aDJ@LE#m;^?`5%Bv!k;zt)WK?qci8unKCa%Cl=*XresPuI3G+OG62O;59ShC&HRNd)Ay8!KuM&gOJ-PJf3sMMIym@wew@M zqrq!tfom)pterP+?mT2j+4D@_nKFv%4#fWR2Mi3(fc7h?F^kR+bb4Iya;jWWyYQO1 z^QH%<&YKat5)m21+@aNj;!MeHgk>K1pQ)g$f>p%45`N0ADIAhk;wQ~j5CXsH!vOwyP;Q))tD}pp6}{Hf5T=%}T`D0(LtzWug19 z$u{&DLWwrWWZAs|JIXbI8vE3!>ufs&5v{llJAgS!Yu4m59er)H6-Q_83SwY`90si? zhGAi$tum}59oF`?xV_VqutBk3+swQ~-fZcCE@;IeISh?P<#W-Jbo7K(17+pn`CE*N zr4+Sx9}<0HAUwX>TYQ^pXCibgK;3;%MIh204Y4`k@_j6e&zGrL+)h~Nq|mP#m!=?}@0~quT~|lTbvE^17q=W; z)ifc7J=PJJS2A`A#lmbJu&$g>2^5Zmt265tpAUwv4K^r#CE3+9ZA52rP|t&hUnvJO zc4qMTWQIV`yJ8V}8CY&#Fe0lmE)Ned@e0psQ&MF0Esvnf>Q&1bx4SyB^O@#rR&9ze z9`e!;bVb_R)ba=XBltdK(z)PLKIQ+i_mF$)u8Nv-9X=I#7!clOe~( zZ*L2#$neY->qHQCizckXOy^1XHMj&03%?9*cps~jwcV%b@L z^%dpJpQ`lC)Dv!x^n@{G*qCF3dQ9&_^>`kMvsE(EvQqIAvtV@Ni^P07t4_u|iE72S ztTi(Suk2tAVMWa%#_qKPJv^o~zqar?ELd!az~hKbtu~x`us?vwxEdCh7d%#m7jkCe z7G?y@4MFP;Q_{}fP=XSjvB+W289Nq;#IS_G3PimhWbqk32CusXiJ{X^R!18aD?t_s ztGYm^S!(ja#0o&fNVl?nq5}q@4$BmsTZFNfT+=OO7Je{(Te8wyw_4IQYce%bWZL9g z+su2yPF@Xdfk5%NPVeH0ER&}uW*-Q}F*#RVS&sE(4iR&;Q$WN8QiN7F_Y(V z5p(5OkMr3;q^k={Ew*+wZS8@A?P6-H7K>J1E@>}r7oO93Z5;P8H>}Ii^BB*t3VnUO zRehW8j_dG*)hoX`WLj`TnVzsQJbDvWU&!v_*9*14;)Q~moUMI{NOU)^idbsATBx7u zk5!l=-@XClZ{Lt;zyE$!md=x%&oT1T~FM${Ag)t#)ocVL&e;C zTX;uc2PR3sTIu11j!PFpXR5L)@;bED+EjB(O>K>JkGung5?C#DVqGk@|1cJx=%K+- zhgvM-We_XRnElS45ZlBynAM0{rQ-cUONhj@H#-e;JlMJQ4l|P7+oLJ z15b^w@Ty<4mEU2z;`EP_4x|m$a$Ol^FJFPm1xT{`vcMoW{Qb+jo#duiZ73 z-iHxAITmcDT(yay-j~gLb!Jq7fwASRLBmhRI(lA5gmL-nz>IQapU$aYy`PwEIh{XY z#~th3ySQCj%XEa!yCmlk+l;H1C{ z0$H*{Td!5`O?-gdI)~^YPD3xIIpx*=aMOt9^v~%Z1dk1fp98c2KRJqjv3H!vgYf+^ zd_Mu3YGKC{+ZSP_Ql0w&pD$C$uTmb3TtJ(1GRp6rM}@ zxv?28SB=}4=29*WUPMIGIk?@5ZYCF}=;$*}eqKdanu}9(?i^f;qN~isDLVd#iGH|R z*2K6D@YaBidH&bWYYs)a65agF?RX#BQ}9QQ90|7b+epJdiafDIV9oArs+^`A+}l(+ zUMH?a)3xT{G@TzfFBDeOtL2&a4)&B;9$P=Jx`1?k);h06l)8Xhn#FBmTt9G*wy9$r zpBt5<4@h2aUMQX@SqYqGPf7fbo(Zg>Yue-5Tv{%AaUPzdLICGqKeE-t;<3OQ&qik9 z2W~KnQ!+7rwx4ThDJ=~mw9KQWbtSa4)*W@-wt#MXsg!OBE~47Hg|y(Ic~iydxzRbO zv}?~o+HUe_q7qsZoJWg*UsSsw z>Y6u)=D8M=t8^}vZZDeVIqaV`);fF<;~z~*t1C;W@?tf)gBz%{?#*Ra-fI?bOoAC27=obvdm@+G?b&t}CO}wQrcdZfeEk^7OK(k_GL| zZLi9OEXfCmzJbv6Eb9UFx(tVL>7dbLWj@9V#!DT>3&x7#b+LpdU?8Rt9#Oo|uXCQk zJC8=`tZoS{KZcPqRX$mkE{)Ffmv~+C=F+@&^b@p+ma~lf39Lg9rkVG$5?c0B300gc zr{b0c3Li$=uQ-kMd9i}{`2>Rdn_Mrf&GMd^S0)eAo%~%}OK2%I0CFW5v=X*Eg>L+^II*opa z=!XdIZ)usF&n`@4Uf7F8uuC$oqpk|{AB>NRT3GC8iC4EN?7}ge-+ct{8p0>%ne~RU z(vRZe!k=f$az4AtOQ^iLbh=~;vZ57L8=hN;d==F2QF!+voKn0|Z{R5p?Yb=mWRXGbH#X&EQ6zLqy*};58cmwhZG=9)fw6ImNqJ%_*8M=S9uv z-Jp8{;V&g`0`boxd|RHsg!tDS>HPe6j`aB}VcY8#(aIPsTl5+~^JRHmBSim<@DuQ1 zC_3`G@NeJdx8s@nNlf}l1^Qhb`fY8AcRtT`H$Wf4=l8HqJ%M1I(a?ntZ>hN*UhB*1 zK2OVBXQ{*serj&SyjTa_FIkY$8|y>K!}&b?l>1|c6UO`i>n25qIKvBgejZ^8VFL4K zG1f7efz^9bttQRv%=Vk?B1u& z=80|xm&VM&rD8&j%w?Z19*)8jB+)_tjpvq5c5K?m{)4_du!lKbj6+XfW2YTB*o;#` zwN1Y_)Q$7JZ-68AzkCoti$<+n2A>JvWgp(z9ZmGnUkJ?|>^76}p16~@@$ESMtO&fN zTI@HJ5pN${5Lz%xOYVExV#rLi#)T4l1HNYMBE@L68+QQsOkP#ByBWjh5iAN_Bl-5& zEN}$6`V(MwE3Yk|2H0qary4zcW&B?IkliJfq-R8`yCSh}oRA#JdW8_Z%h@hn9en15 zs~K8PZw)|wI0C?lSC&eh{e%M0v}(lFXu}qEblHwWF<%o76|jWl7H~Gp{xGx`IA{mC zecmW_6P-x?eg^EF>ik$KfZmfWQTmJN7terlFHWm{+*vq6ZCTno8h!i2EYCp-XR%Ff z@lKkQT7ey(o6=)==G|`YG^lnrpN?o@jrQ{JySG0Il~d)9O(o%m-N?=ok>I zH$l%!`aQ8w!rmX|6G7#>budR0OpiAKro~yMkN@yM!=QKzxUnpEvp1`>e(JI@d&t&) zd7lIa)k>=D2`PIQ_he{n)TF9vKA4rDITYw@gewpnj)<-W)%HlF%crPr%G6ow=Bz4v z7Y?CJLp%g}K48}LE^yU@jhn@csh^`3(q9#u&CC^s&bK?huctksPDs{gN|G){?`2Ku zQ4Eyks!v@0llkdW8l-64JF@lJ1=luuN@{WPLOI39+kJ%&+>z3>>&W4I?<+ik(1 z7%aNQXsVh0QSo+@Ee|#49%}mZV{C0-U6KO%;o^a%FSlN>sQ9JI( zIY;%BG8$05V$hT**-gzJ$d)ZBMEkQimPb0;H26tBu{(VH>VfzDEG1f7Tf0ptX?q{e zm6Tc3nJTc(7;gu5UyK01TVW^j~locow z{TQ~MJyG@1*?Qr#uMZw8-I~vTIp%AqO11kNS)R;{rAzp+l8L>ScG&SiOv+s#9MzP^ z{zu9y=tn3!W7Cw8<(Yomp=0MN=02943-RrZL=N^uv8$2QpfYi(E9Nw9H;41n&#Z7mD|X;p`iPi&DnC?1H;DTk+9i455O_wR0d>BQF%f39t5rr*p+EcB~sC zDq$NGrRMIA%R?l#3pXwxs~6tc?Cu^d`hy-j*5|cj@X>`!aR9Zzk_m-xx}5v&AZ>PF z^+E$kDw_*Qbn~~sM`K$Y6|gx*b*>$HmeRy~L+vqm7qj}~^ntlNs@MVkh`o=ER}<_K zB_Zt&yS?WCyyA?Mdv-S8wR7*LTCMX^oC7!d)bVUbDB#n|gSOTJA5ZA3Eb7P@-kkA7 zEYjzLi#h3u2i&O_y{@iJOo{ZgA^|J&P34FCc`O7up?Fa5U zZI(ade&e^=@%9C;ov?C|5}zG73biT6DEJ_eSj-V6MfwL?Kr`@tFSqO6yK zU-<+DZOj?kK?t5s`W}j6i3><@)Qm?YyVH*@>j&ZA!2YS~&P@QqT8B|8Je=T2#$KMr z(Ma)wYm)MFPr!;=emMO2wdiUek9CIgul{TsU{CG#E1%nY{zl^$ilW~qFu$D|gwC{+ zH;>LM>HT?hFz)J?&ri^^g35DRKI_98n%-mye)_L9KKYo2rks9Be^KDXqdML6s~N%1 zCV(aVGufNVf3|!k-TIQwH}MG#Cj}lAJsCQW&{=(&uHgv{lX-M$d7hATlYcUg&fsT* zGk>Ma@gCN2LSTNrQju>qI=9eG3m=c@EuWv!%WQN;FGl|p1^6}O=`X)L^XwJ62GMiB zK$C8u|0?OCroILE7=Ha%NjD&LbVRp@$)69CS4lS^bk;2V(pO10DRgP!*HD1()K$_= z8@fN#^=%NCUtf}On0`>`1YSn>DiQyS`NGdjSM71Cjqbh>UtBVCLN=AHx%iuIs;SR9 zz{0N`ex|ZS0FheXsT&b(xHN!kDaNyuJq9-@2{9FDH)Gr!u=RC4_VvYGvCg(=6t1vV zRou3%&O{;_-*VGUSfj;pGQ(F9Ya+1&H-+Ny9(z-L{ieG0GEbOG$HrdftF1}wYW}fH z`dzQwFAbl8{;Q*#e$Um>QG>DEQo_3>-yh}XuQCGU!Kq(px(4yHOaEJLK0_YOliOnw zenW6T{7chIw0vg1GsPEbFY6giUug&hPTYLudghV+`XBS!t6{U2!!PjHU*Icq^t?uT zh%U;#&u`if{(@@#mEV3Bq0fI4^Pd$xRcUC_2c+MYx^v4b)E_R%df;fD zT0D(?~D z1qJxIgRl-cW#UTIdFaZdC!g0cnqVsYi7Ix|Hcx(hrstpevPf>xHh+IPwTx(=2ooQm$9@ zm%qL+_N7_|*%YdlvaD!%ygz=9ZeVLl>kTGqbdBtMKa- z`IZ&nmlV1dDYsC+@(bOn*~XE`w_z6jbqc?f)T5AHOA5aiMZQAgXiE5<6@G>K%Y@Jk z3co_-P8#`yZg~OynRR8ql)I<^-IUbBFZ@;%pz}((e&Kidc+(k=N=n9RG2BEts@)c?~61oSazZ9DH2SmOmDYsC&%?RD|LRY9i zM}@9(minfJ?&(?TOS8}!Jx@z}3`qMHYPXc|YZ1CadcGiZGot50_CCM6z5e{$)qood zj8CKS`A!zzY3Vo&egg3cz#`la8bI86Pl)dn-3Mck2A<)A2p17&_)&xz#2N0d!kw{6 z@C97^Ce70WSba0@`2mmjc{+ix199we(8kR~dzl{aF$DJE#~Pomhi3-U;Jt^wfWS0( z_nG7%>~(T@;VZ_|M0z7NBHLD&l#hDGh5LEH`aQ;vhD^S%$? z`{939@E~XymIuL$=>c!-fXs+H@8>x0?(iL+Pa!-H8irp%m`0r84-w$@MALv5JHe0R zfX5zy{t%r+=}ot z;tWT_xRQW4!_OfwJ`H$N41Vi~djOjgTF=h=GS0g+&U-d||K`UC8@{RG%ZN81&Tx4T zJYf)LxDmmRcmv=m1a8v_z-@*qV2;587G&qx>pBVfRqGu4827B(b1^>j4`D34)o_+HQ$!T@Aucotz4aqR8VFAJ*Qy;@ePPGJch6p@g(5l_d~CUJMZl{@AUB9 zo`)YsKF}~6L3kW-hF?NB#d$x7Hb7vVF#O3QkPmoh$jJ;e;9Q^9Osnu^J8cq#GUtZ_}+&O+(NkHaq$aqP3v zR}k24OaiWXT(=SS0qIc$F4uVvhwtnJJ^{JDt>K3dUxPTqlL+;Q;|!BFKB4uAb4nVL zIL;(#{U`Mp@c?!w(YByZ1AY&o74a!R&ydc`aMv*A0pObezmAYZoZ-tOXbZ$?6rYAi zVEG#W69_Cb!(GQA+jo!`@N)vFX1F1x`7msdIKzh#Sk6ZPm!Ht_O27$@LpFxb37+B05_dm| zdE_MSD}&}fz(EAIISldbS{1(l_`cJypP=Eps`H=HGL!@2yS4N<@c6zhJ%liTIKFYK z?nMnE&iAAE-qRp-%aHFpahxIFTjDrFzI(I^^bG3}f{4%lbHM|LIP~Yz;vNzZo-5$z z7oq)-#`mK!bT0dCy}h@)%X}o})^!_eJnQ%ag^?h9#&2B*&&%rib@+50ytLZzxi0(G zb$xbx-R-wklx@ZLo9ym(e6JJ?!tqL$Yr50^>!{~R%)+M80N6QgHG)1&7{FN{_l$5$89=sQqN zYJ57?bkcva^W^l&(o^nJm8YyzRi}_JjkSskVIrV;N*>xh3OI?_9GbR<1;eq?gw(#Z74%!qrma?~2F zA8iFOAai(&H?b_jv2^-sAnplgCrX&mW&WK7D-VIE__~ zSz}dW4P#AX-m%WH=veRAh4Ja}%2Y$DH`Sj?r6y8mQ7@5$D{2@$Ie2p7Wc?}ssm@c;Q@y83PuHLJo^CxoczWV={ZnV3a-XR@ zQ-7x6jQ>pMnWJY0&P<#+d*;HKDU2=`O+h92ut2gZ=}CH%t;ybGe=?a&CC?`(lhetW zWaW@G)G*XE)HxI#8WtaRKx?isHiZyooK_m3YPPmNEE aPmW(0pBbl=m8wcLrM$3!um65mHSj;?bf0_x literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/annotated_types.py b/libs/win/pydantic/annotated_types.py new file mode 100644 index 00000000..d333457f --- /dev/null +++ b/libs/win/pydantic/annotated_types.py @@ -0,0 +1,72 @@ +import sys +from typing import TYPE_CHECKING, Any, Dict, FrozenSet, NamedTuple, Type + +from .fields import Required +from .main import BaseModel, create_model +from .typing import is_typeddict, is_typeddict_special + +if TYPE_CHECKING: + from typing_extensions import TypedDict + +if sys.version_info < (3, 11): + + def is_legacy_typeddict(typeddict_cls: Type['TypedDict']) -> bool: # type: ignore[valid-type] + return is_typeddict(typeddict_cls) and type(typeddict_cls).__module__ == 'typing' + +else: + + def is_legacy_typeddict(_: Any) -> Any: + return False + + +def create_model_from_typeddict( + # Mypy bug: `Type[TypedDict]` is resolved as `Any` https://github.com/python/mypy/issues/11030 + typeddict_cls: Type['TypedDict'], # type: ignore[valid-type] + **kwargs: Any, +) -> Type['BaseModel']: + """ + Create a `BaseModel` based on the fields of a `TypedDict`. + Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys, + we raise an error if this happens (see https://bugs.python.org/issue38834). + """ + field_definitions: Dict[str, Any] + + # Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions` + if not hasattr(typeddict_cls, '__required_keys__'): + raise TypeError( + 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.9.2. ' + 'Without it, there is no way to differentiate required and optional fields when subclassed.' + ) + + if is_legacy_typeddict(typeddict_cls) and any( + is_typeddict_special(t) for t in typeddict_cls.__annotations__.values() + ): + raise TypeError( + 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. ' + 'Without it, there is no way to reflect Required/NotRequired keys.' + ) + + required_keys: FrozenSet[str] = typeddict_cls.__required_keys__ # type: ignore[attr-defined] + field_definitions = { + field_name: (field_type, Required if field_name in required_keys else None) + for field_name, field_type in typeddict_cls.__annotations__.items() + } + + return create_model(typeddict_cls.__name__, **kwargs, **field_definitions) + + +def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']: + """ + Create a `BaseModel` based on the fields of a named tuple. + A named tuple can be created with `typing.NamedTuple` and declared annotations + but also with `collections.namedtuple`, in this case we consider all fields + to have type `Any`. + """ + # With python 3.10+, `__annotations__` always exists but can be empty hence the `getattr... or...` logic + namedtuple_annotations: Dict[str, Type[Any]] = getattr(namedtuple_cls, '__annotations__', None) or { + k: Any for k in namedtuple_cls._fields + } + field_definitions: Dict[str, Any] = { + field_name: (field_type, Required) for field_name, field_type in namedtuple_annotations.items() + } + return create_model(namedtuple_cls.__name__, **kwargs, **field_definitions) diff --git a/libs/win/pydantic/class_validators.cp37-win_amd64.pyd b/libs/win/pydantic/class_validators.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..ff6f576ce4ba8615dfc203387eb4a23bcd38883d GIT binary patch literal 183296 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

-I<(rMoS!>(%0#jRB9rH8@#rh%qfkK+#2x{t_V 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 Any: + return self._tuple[item] + + +# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached +r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*' +r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*' +_r_255 = r'(\d{1,3}(?:\.\d+)?)' +_r_comma = r'\s*,\s*' +r_rgb = fr'\s*rgb\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}\)\s*' +_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)' +r_rgba = fr'\s*rgba\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_alpha}\s*\)\s*' +_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?' +_r_sl = r'(\d{1,3}(?:\.\d+)?)%' +r_hsl = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}\s*\)\s*' +r_hsla = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}{_r_comma}{_r_alpha}\s*\)\s*' + +# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used +repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'} +rads = 2 * math.pi + + +class Color(Representation): + __slots__ = '_original', '_rgba' + + def __init__(self, value: ColorType) -> None: + self._rgba: RGBA + self._original: ColorType + if isinstance(value, (tuple, list)): + self._rgba = parse_tuple(value) + elif isinstance(value, str): + self._rgba = parse_str(value) + elif isinstance(value, Color): + self._rgba = value._rgba + value = value._original + else: + raise ColorError(reason='value must be a tuple, list or string') + + # if we've got here value must be a valid color + self._original = value + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='color') + + def original(self) -> ColorType: + """ + Original value passed to Color + """ + return self._original + + def as_named(self, *, fallback: bool = False) -> str: + if self._rgba.alpha is None: + rgb = cast(Tuple[int, int, int], self.as_rgb_tuple()) + try: + return COLORS_BY_VALUE[rgb] + except KeyError as e: + if fallback: + return self.as_hex() + else: + raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e + else: + return self.as_hex() + + def as_hex(self) -> str: + """ + Hex string representing the color can be 3, 4, 6 or 8 characters depending on whether the string + a "short" representation of the color is possible and whether there's an alpha channel. + """ + values = [float_to_255(c) for c in self._rgba[:3]] + if self._rgba.alpha is not None: + values.append(float_to_255(self._rgba.alpha)) + + as_hex = ''.join(f'{v:02x}' for v in values) + if all(c in repeat_colors for c in values): + as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2)) + return '#' + as_hex + + def as_rgb(self) -> str: + """ + Color as an rgb(, , ) or rgba(, , , ) string. + """ + if self._rgba.alpha is None: + return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})' + else: + return ( + f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, ' + f'{round(self._alpha_float(), 2)})' + ) + + def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple: + """ + Color as an RGB or RGBA tuple; red, green and blue are in the range 0 to 255, alpha if included is + in the range 0 to 1. + + :param alpha: whether to include the alpha channel, options are + None - (default) include alpha only if it's set (e.g. not None) + True - always include alpha, + False - always omit alpha, + """ + r, g, b = (float_to_255(c) for c in self._rgba[:3]) + if alpha is None: + if self._rgba.alpha is None: + return r, g, b + else: + return r, g, b, self._alpha_float() + elif alpha: + return r, g, b, self._alpha_float() + else: + # alpha is False + return r, g, b + + def as_hsl(self) -> str: + """ + Color as an hsl(, , ) or hsl(, , , ) string. + """ + if self._rgba.alpha is None: + h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})' + else: + h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})' + + def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple: + """ + Color as an HSL or HSLA tuple, e.g. hue, saturation, lightness and optionally alpha; all elements are in + the range 0 to 1. + + NOTE: this is HSL as used in HTML and most other places, not HLS as used in python's colorsys. + + :param alpha: whether to include the alpha channel, options are + None - (default) include alpha only if it's set (e.g. not None) + True - always include alpha, + False - always omit alpha, + """ + h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) + if alpha is None: + if self._rgba.alpha is None: + return h, s, l + else: + return h, s, l, self._alpha_float() + if alpha: + return h, s, l, self._alpha_float() + else: + # alpha is False + return h, s, l + + def _alpha_float(self) -> float: + return 1 if self._rgba.alpha is None else self._rgba.alpha + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls + + def __str__(self) -> str: + return self.as_named(fallback=True) + + def __repr_args__(self) -> 'ReprArgs': + return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())] # type: ignore + + def __eq__(self, other: Any) -> bool: + return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple() + + def __hash__(self) -> int: + return hash(self.as_rgb_tuple()) + + +def parse_tuple(value: Tuple[Any, ...]) -> RGBA: + """ + Parse a tuple or list as a color. + """ + if len(value) == 3: + r, g, b = (parse_color_value(v) for v in value) + return RGBA(r, g, b, None) + elif len(value) == 4: + r, g, b = (parse_color_value(v) for v in value[:3]) + return RGBA(r, g, b, parse_float_alpha(value[3])) + else: + raise ColorError(reason='tuples must have length 3 or 4') + + +def parse_str(value: str) -> RGBA: + """ + Parse a string to an RGBA tuple, trying the following formats (in this order): + * named color, see COLORS_BY_NAME below + * hex short eg. `fff` (prefix can be `#`, `0x` or nothing) + * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing) + * `rgb(, , ) ` + * `rgba(, , , )` + """ + value_lower = value.lower() + try: + r, g, b = COLORS_BY_NAME[value_lower] + except KeyError: + pass + else: + return ints_to_rgba(r, g, b, None) + + m = re.fullmatch(r_hex_short, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v * 2, 16) for v in rgb) + if a: + alpha: Optional[float] = int(a * 2, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_hex_long, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v, 16) for v in rgb) + if a: + alpha = int(a, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_rgb, value_lower) + if m: + return ints_to_rgba(*m.groups(), None) # type: ignore + + m = re.fullmatch(r_rgba, value_lower) + if m: + return ints_to_rgba(*m.groups()) # type: ignore + + m = re.fullmatch(r_hsl, value_lower) + if m: + h, h_units, s, l_ = m.groups() + return parse_hsl(h, h_units, s, l_) + + m = re.fullmatch(r_hsla, value_lower) + if m: + h, h_units, s, l_, a = m.groups() + return parse_hsl(h, h_units, s, l_, parse_float_alpha(a)) + + raise ColorError(reason='string not recognised as a valid color') + + +def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float]) -> RGBA: + return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha)) + + +def parse_color_value(value: Union[int, str], max_val: int = 255) -> float: + """ + Parse a value checking it's a valid int in the range 0 to max_val and divide by max_val to give a number + in the range 0 to 1 + """ + try: + color = float(value) + except ValueError: + raise ColorError(reason='color values must be a valid number') + if 0 <= color <= max_val: + return color / max_val + else: + raise ColorError(reason=f'color values must be in the range 0 to {max_val}') + + +def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]: + """ + Parse a value checking it's a valid float in the range 0 to 1 + """ + if value is None: + return None + try: + if isinstance(value, str) and value.endswith('%'): + alpha = float(value[:-1]) / 100 + else: + alpha = float(value) + except ValueError: + raise ColorError(reason='alpha values must be a valid float') + + if almost_equal_floats(alpha, 1): + return None + elif 0 <= alpha <= 1: + return alpha + else: + raise ColorError(reason='alpha values must be in the range 0 to 1') + + +def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA: + """ + Parse raw hue, saturation, lightness and alpha values and convert to RGBA. + """ + s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100) + + h_value = float(h) + if h_units in {None, 'deg'}: + h_value = h_value % 360 / 360 + elif h_units == 'rad': + h_value = h_value % rads / rads + else: + # turns + h_value = h_value % 1 + + r, g, b = hls_to_rgb(h_value, l_value, s_value) + return RGBA(r, g, b, alpha) + + +def float_to_255(c: float) -> int: + return int(round(c * 255)) + + +COLORS_BY_NAME = { + 'aliceblue': (240, 248, 255), + 'antiquewhite': (250, 235, 215), + 'aqua': (0, 255, 255), + 'aquamarine': (127, 255, 212), + 'azure': (240, 255, 255), + 'beige': (245, 245, 220), + 'bisque': (255, 228, 196), + 'black': (0, 0, 0), + 'blanchedalmond': (255, 235, 205), + 'blue': (0, 0, 255), + 'blueviolet': (138, 43, 226), + 'brown': (165, 42, 42), + 'burlywood': (222, 184, 135), + 'cadetblue': (95, 158, 160), + 'chartreuse': (127, 255, 0), + 'chocolate': (210, 105, 30), + 'coral': (255, 127, 80), + 'cornflowerblue': (100, 149, 237), + 'cornsilk': (255, 248, 220), + 'crimson': (220, 20, 60), + 'cyan': (0, 255, 255), + 'darkblue': (0, 0, 139), + 'darkcyan': (0, 139, 139), + 'darkgoldenrod': (184, 134, 11), + 'darkgray': (169, 169, 169), + 'darkgreen': (0, 100, 0), + 'darkgrey': (169, 169, 169), + 'darkkhaki': (189, 183, 107), + 'darkmagenta': (139, 0, 139), + 'darkolivegreen': (85, 107, 47), + 'darkorange': (255, 140, 0), + 'darkorchid': (153, 50, 204), + 'darkred': (139, 0, 0), + 'darksalmon': (233, 150, 122), + 'darkseagreen': (143, 188, 143), + 'darkslateblue': (72, 61, 139), + 'darkslategray': (47, 79, 79), + 'darkslategrey': (47, 79, 79), + 'darkturquoise': (0, 206, 209), + 'darkviolet': (148, 0, 211), + 'deeppink': (255, 20, 147), + 'deepskyblue': (0, 191, 255), + 'dimgray': (105, 105, 105), + 'dimgrey': (105, 105, 105), + 'dodgerblue': (30, 144, 255), + 'firebrick': (178, 34, 34), + 'floralwhite': (255, 250, 240), + 'forestgreen': (34, 139, 34), + 'fuchsia': (255, 0, 255), + 'gainsboro': (220, 220, 220), + 'ghostwhite': (248, 248, 255), + 'gold': (255, 215, 0), + 'goldenrod': (218, 165, 32), + 'gray': (128, 128, 128), + 'green': (0, 128, 0), + 'greenyellow': (173, 255, 47), + 'grey': (128, 128, 128), + 'honeydew': (240, 255, 240), + 'hotpink': (255, 105, 180), + 'indianred': (205, 92, 92), + 'indigo': (75, 0, 130), + 'ivory': (255, 255, 240), + 'khaki': (240, 230, 140), + 'lavender': (230, 230, 250), + 'lavenderblush': (255, 240, 245), + 'lawngreen': (124, 252, 0), + 'lemonchiffon': (255, 250, 205), + 'lightblue': (173, 216, 230), + 'lightcoral': (240, 128, 128), + 'lightcyan': (224, 255, 255), + 'lightgoldenrodyellow': (250, 250, 210), + 'lightgray': (211, 211, 211), + 'lightgreen': (144, 238, 144), + 'lightgrey': (211, 211, 211), + 'lightpink': (255, 182, 193), + 'lightsalmon': (255, 160, 122), + 'lightseagreen': (32, 178, 170), + 'lightskyblue': (135, 206, 250), + 'lightslategray': (119, 136, 153), + 'lightslategrey': (119, 136, 153), + 'lightsteelblue': (176, 196, 222), + 'lightyellow': (255, 255, 224), + 'lime': (0, 255, 0), + 'limegreen': (50, 205, 50), + 'linen': (250, 240, 230), + 'magenta': (255, 0, 255), + 'maroon': (128, 0, 0), + 'mediumaquamarine': (102, 205, 170), + 'mediumblue': (0, 0, 205), + 'mediumorchid': (186, 85, 211), + 'mediumpurple': (147, 112, 219), + 'mediumseagreen': (60, 179, 113), + 'mediumslateblue': (123, 104, 238), + 'mediumspringgreen': (0, 250, 154), + 'mediumturquoise': (72, 209, 204), + 'mediumvioletred': (199, 21, 133), + 'midnightblue': (25, 25, 112), + 'mintcream': (245, 255, 250), + 'mistyrose': (255, 228, 225), + 'moccasin': (255, 228, 181), + 'navajowhite': (255, 222, 173), + 'navy': (0, 0, 128), + 'oldlace': (253, 245, 230), + 'olive': (128, 128, 0), + 'olivedrab': (107, 142, 35), + 'orange': (255, 165, 0), + 'orangered': (255, 69, 0), + 'orchid': (218, 112, 214), + 'palegoldenrod': (238, 232, 170), + 'palegreen': (152, 251, 152), + 'paleturquoise': (175, 238, 238), + 'palevioletred': (219, 112, 147), + 'papayawhip': (255, 239, 213), + 'peachpuff': (255, 218, 185), + 'peru': (205, 133, 63), + 'pink': (255, 192, 203), + 'plum': (221, 160, 221), + 'powderblue': (176, 224, 230), + 'purple': (128, 0, 128), + 'red': (255, 0, 0), + 'rosybrown': (188, 143, 143), + 'royalblue': (65, 105, 225), + 'saddlebrown': (139, 69, 19), + 'salmon': (250, 128, 114), + 'sandybrown': (244, 164, 96), + 'seagreen': (46, 139, 87), + 'seashell': (255, 245, 238), + 'sienna': (160, 82, 45), + 'silver': (192, 192, 192), + 'skyblue': (135, 206, 235), + 'slateblue': (106, 90, 205), + 'slategray': (112, 128, 144), + 'slategrey': (112, 128, 144), + 'snow': (255, 250, 250), + 'springgreen': (0, 255, 127), + 'steelblue': (70, 130, 180), + 'tan': (210, 180, 140), + 'teal': (0, 128, 128), + 'thistle': (216, 191, 216), + 'tomato': (255, 99, 71), + 'turquoise': (64, 224, 208), + 'violet': (238, 130, 238), + 'wheat': (245, 222, 179), + 'white': (255, 255, 255), + 'whitesmoke': (245, 245, 245), + 'yellow': (255, 255, 0), + 'yellowgreen': (154, 205, 50), +} + +COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()} diff --git a/libs/win/pydantic/config.cp37-win_amd64.pyd b/libs/win/pydantic/config.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..689773e443074c6c715ceb0bc99b486f7d8476d4 GIT binary patch literal 79360 zcmd?Sd301o_P`&Oh5$Al!RN#UjT#k16pc7wWK2tfucd=Q7Eu<1h{P3yj-s-NNk-de zt23kHI_~S}xXd^Xve;n<5l~b_alvKAZd(+W0bKj{xwqcy?sUS;e9!s)@jK_|k@Q<` z-MU-dx^=5wRrNWy7IY{mDCo$)bh@Bm0l)HJh5r4|e~Q`_6m&m$VfTU;x~xBBLA&t! zLq^qIIJ0E>jA>J6OuDpW@}$cyn-(cKZ%WCG`pZf#ysV`1jFBamPCI|fF@As7QWy2y z>8Fi4Y41Lry?iwA)POg)>_QY9DRYAe|zwK0T-O2k-_F#4t99q(? zefL8OUg60V=4r)xev9<_TEt+k+ZPmc(ns0f0(mRK<-hg?wcMCO-cRd$^Q5LzyMpnQ zd79tzr7%Tz%WqA)f=Dj))7uq83JMOC_j~y7f$!0&66ykMD=66F($!XxV zZO589pdD{EgaJ=mU%n2Q7Lt1YfhO$qI)H`_$^lyI0@-#`b7ZIhG;Iys{3F2OnD5g! zrJ@aCkn|46!le~eWzBYUc4>(nv!1Zp6(q{$7ZiXzx(@h-H`0QVcS<#m|6M^F^ObGk zXLV(%+E6&=KS1scHsAGvJZ2TUASGgJw$mkT9Rzy|e+#nIaJwPhK61J3R25P9gSQ31 zKM&NsmIMsIxt>0j@-dk{CGHt5=K0JY{Uy*cI zB>mI~!l{eV?`35<`c1;TAge&XfyO?9UX395>l~mJE|3^Opz#HffC8O-Vh+&#E>KMi zOF0>gXk+0fp()=|E>TPQr6-osWuCE=y?|es5KH-|3fljippE&uZ01KSMa%$YfBhEs zqDzoZ5acoIOL-(}6|bX~@*Vxf!cTL|Zn8nP*oE}W?vWg1MF!b}t&lw~$Ubn*?&l7X zDYkQ(nB7!?bo_;&hL+de_9A9CLEglC!#(Vt*$I7V>ny>y%b4A_9|)xX5ef$v?8ocG zh0mzjb;W!x6Qae`!kACltBIHYE*0KedM$7mTn-jz?oO4QWQuvwiI2>AeMQVS&rwO?C zP%GCC??TSTw6)KxQf!Bd(Kw=`QekZHJVXzOaYR#)gt0+L5(`@?oTWDacPO@+fg9|? zQNtE7vJnE)v@>wC&luV5I0gYWW?|e>7zbN|F5W4^$R3$3Ms`)#g%xyg$;YLZ48h4} zz4c3sfzs}JgCDUsP>a3ImwVUV`cV%k=ei(S&(w&YTX-_KQ^P|IzqF6w6x1_S3Z&!z zC*r_Fz7&0eN=)P=c@y*X^{{&;B37QZP7r+6;KMV$_MSlcmr^*m;7VR6hCVGOlANn_ zqZiRnc`u4w6^{LNLVNuiCKS8g7Bqx6Jg-p2J`APL)}e6 zol{W^R1;9uPDp!}5^Ycq8}RO7XSwaX9dcex9HQbGUs@A##+8Jei+hKhnf*h~H3LJ= zC&6=r6NBTiP1{)!j#d;0M`=eFt&=X!j!sY5&U!mKYa2c=9Ie|Dj!u8As%&RCI%`dp z^Cix$u*&%tHn8&;qAk=|7j|yAmFnOn;tQr-tl7bruMqW0 zxEXRzE5sr|^7?TgajrGxo2C3~Qht9auhvv=*-pQUw-yvAhFOB)O5(~;!?Eq^ySjMN zHT`Lio7ajAM;6+H+Si|MJJ%FT-yF1^nMJm<&Q5o>oe%7EC%d6fyZTPH)1E?Mr;@(G zc^eOysKwFQAf;W@(|%4Xv}q|d{Zrj-XEWs!74wCn9o5b+n6?tN`CW*b3tQI~6z3*0En43JZcSKPX^hsSQNEW)6p?uxQ1aN)d(MfHBZoZ}>Yfgcw0*L*TZh zO=|;RSvDp<3}aZ zMSg>l(Wwi_*9&sTI?y9yUE(fWi%amYFr-7@DJY1W0)%ME?<+u~_E7WO7Bz1?y3LwzSR*y7xfaEvD)Pew z={Z{1clIh?$HsV#Zz7Fvt2Dm3G`PvlR&ib6E| z!d=%s0TcXJ6lkhJ)3$L!_)etX4H-yJ*3o#Qn{xH~+O|62tfO>m9V6vTxZ<_p;Y6=Fj>a?PUyH?f26dAPwN46mq0+SVj&N+6Tw(C# zi+GVb52nvVj@8Zzaj(`hfY8QxeTlpt5_Wu(k%@@Uu_pBu={OmXO9iBa7Z0d;(@kzj z%$X%NrQ8n>zI+)FaiIf2{3wHzJh4^Nn-sO`cChmERNv!s%(f=KY`#V0qY0&}=o8~BjAr`Ia`u%Zm=zzRrZHFE0ZO2VjOu{}2u8DCoXipu1e4Hpj~+ zt10G>g3Ne1!GJ!|VTN|8X1x45hT@Hv|14-@z7A{o$%#r+;w;^$^h0T(F)JaDWE6zY zfGc9WFPn)X*Na`Y94|j3kdFThP!p9tAjRFN^bvUz^W82#g1u!_dY|C?0DJ_PC+TR( zKa;}21rl5(o_kOu%KY*2lfuBB#>>fWl+3D|@$)E2jf6`JM~@tH79*F@I(CU&Qu>iS z|BD*{t}6S~F1JR3w95G+9G&u2(D{y^&w|c-{LHIze&T0Wl|Fo%%PRv-Pf-kZpTJAl zPO#V>~wqES(bboF{Q1eg?W4*5%|W$v(u#b4hi>YQFO!0lFtJb zzYF?;OHS^J(iap8$f?=BqeMt+Z%0RemE7H4Fdiy2M60f}jaw;-->;=Fm(o{j>FQ}| zYj#}lxbcL6m?;or6{14=lf6L{8Nkkc; zpIa1&eTaW1?q*zb;1=!gs;1iMW|*Fu>Te*HD8%>=i}POZO~{`{Kf-vR@gM{yL0)tM z`b$A*#INB2aeA;2pJal-_i)+k50$~@`ZoHIZ=o-&eMq2j6}8&28WiVybqSY@0>U9@ zZn2C4hJ>7nMIi}9I)|Nag{&5Vi0_R<)Q*sHV(d)H>NsFa{XyC8E!6`;o(2poi4*F9 z5jM648c!2uwW|mbTJu)o+45c=p`)PlQ_y)e=&Yy++_t*f`Hsi?LmAOHSN$P9a9eY? zHQhGWchmmtGKvtpr2{vlM0Ie%PN#!QUK9gq27)LnzlW-Jermhxu%mOnqDdyz*=qCM zmqrD%kLoi zZy{X>ycD}f0jb4;bdBxwNNjsRNRM<&TfgODG7La)$sLrFp{jHEAcos&_Xz22j4ROC z-bY!~9%$O#QLw0yCBM*X|L15nbp{^gBV!51qnxkRiAU+G?5RijwOmH6Ed)n)(7StR77w_G=hKMr)dqiQ+P^uLQ3+aEVyJdbtm7^DBizSD2rL6RtVj6Q9VHh zqyY*#JCn}?NB8ouHiWl`xIQ{qS`J+&>72y9vr7tq9dXjhUZFK4=telpsLP+rf0AL zu+oOthhykAHhOB>+RAG&^UK7CIXsnyQia9W%VpS^lTL1fLgg_wNBW1gl&G)4xTl87 ztMSSMrDQDF4l1UGobY;lFR6@RwyWw`AE_!^OO+o|<=cr#v!teaT<@F@#bjTmeCiqy z--$TtOR|VRq~0vzDok^)HK~V`sh^3pq%~rGw88&wb41q7{91Ozk zGFAijnL0S4i~0|-n6&i)Ml2LHU5e|BKL#1WB|ajXWRlAe;xyrK7u)G9Y)LL@&S$ps zA)Q6?OjU?~D?-6$&R-@hU^73yn6a6iVg~mMZ{_~+BD+#d?GK{vn6;M&zpf>GY6dWG z^9*DnoH_mZ+m6WYB#e@z<_q!@s9Xbz%a@ABSe{q@_}~HzPpY^|-#?u9zK1s2jgW>E zu8cnfEI#q@H56%Uw1CjAY(wIVZIExv;a{7hAjwOkWl8E}JHcv3(lP&qv_c)Oyp=eH zEaO>#~Mjz4$exJ(*e z#w#UbB;zg#&uR(wz9G?0Kd8FUfQ)|r-cmuJ;~ny(6PL@3;B=WBBDp>*99>*`Ew96* zHoOcy?w$CO=DAwLQ>qzycRKI@HK3s-60h5&Lx@QwL+^8w@uc^zXWqtk5^iBH86Tpz z^B<_;h8g8-k=X^1RG82*(1_x3&sK5fk<0XLX+wR$Quaqm~S8HLvn^YKWT?%CVj+zV3*&^ zqZY%R^xp~!UY-Qn)R`fs2Lp|B75h+`t63z_j(njKaECn4%bS?*A+Ls(L!PGu z=L~StbDy@7pFC>~<4|IL3w*%^f2KMH%}?B>hi8cPhg7}W=z zH^n6S6DbC6TPD3@Z{B?KS9wEro4FydU3J< z%Q4<2q>1pfH5A(1zEBEpTu$M?XyJuY_*n|uJOLeGvdX-88&xDRMT&KLDZV*V2Zea!l;IwN|S z+o8@2C zM{sahgc`HHp|9t4Pq0?YWSOA+Z5HKzf>I|<^HqUeh*(WTUL<}FK1&7MCrmh2@jl{Y zk@p>0c^s0ql5z(DJz>Z#b%d1K2m;&rS##c+Y-^$QGD>+`*Lq{tt;kKuy&wB^G4&Np zO^S(_5(zX=4hdAiVl7|_eV_2qg9Ic0A6*a7@oCjo8|wki!74Bb6M~G!%eM$Y=y7qE zGMRXkX&nZxp9^Q3;a#is0Pm~yx4j-<&^IXhw()A_T^HUf#`3rti?@Iw*{rwz{!&TI z7sY?(h9Z~Cz2ruTBkHAX#;nU+kZk5P@)ViUJHef|9^eFlbo_524vL>B#oaLED0vg} z9pGW7SDM*}nlSCZ(>j8xR%zA)tLC|77z@H%j z(i@Z@eAtadFYbDPUmNPysJqhj0QEq%To3RE1O9(q4Aqvs=Chfqb92|3m&fp!Iwa?v#aW!uXb*{;c53UG$dD^Us7Ao0vtBU8GQZo&AL zy-;wij|9oN9Nbn<(AVX1K z&E7_W0d=v1gWM+ZZov6R?}ZTO*iNr77q;_8*cpNlr#&ezcT?0-bSlK(s}KiwP-N*y zKk3&PAv3A#+*JC6qO(p!6*3UdNkRTb78RVr_*E2x8vSMIVElWLTzBnN*Chsxb!$^( zP3ih)T!8H$O7>sxI<2qV zWofj0Btq5oI3kf7?u6>xRuO_R>@J$7zYNCxpu?CO=^_^~_D5E2^it~PcnO}|i8|)8 zWumU8KN-E~3#AWe@wU3YuycOtKsa!$qfsL?MxQQ~KACipj7DBTtCA;8Nfekn$?qYbwQf4iLuf$31wym*o5QXb&v}!J_SJAqS5w`lL?wQ?M7?^^B06h$ z?hDV!sZ{n7!xB`z@iD1adv{blK;Bj=DZWRMU7s&bzb_01DnFD} zcb?wt=vgJ$y|0^;r#9b@f+>0$5lFBQxsBpB$GW|l>`SFy>2uuh5%O&zze#PJxU}5Y z1Lo?>Y?`g@{JWJ|Im1iJwpTh!Wn9>MX|jE=VOvFHbZqpz)TPwg=~3Z)eeV3ftOWBpr#@zaH#f5X4Cv|(Fkl1=rU7pge+BK@>b#DOf{UeFnc z!pF@l<}_8u<-O zJcRCDlD6OoqzR3Iq|}twO~`*UOwd{r8^#IWY7l^*E#N2SJ6owq`k};9LWb73eh$Vb zQf^T*e9^v2b(G<@5y!Ls#?o7Pjo-x%io#B9p&j*n8fz8psEz8wByXZNZn(GP{0ShB z(oxc>yn2PEw}%#)v8z_u%rG%yi8RRGsmt?uY8gd(S?&Kl@rEAW5Zg!>B>w_q!T7tl zO}IaT5ym2-6Iczi$gveoZKnqR4^ed6ZmA_ZUQlej$0xKLH{>pAkO)WO>#-7F*Ag<< zNyyA9+Hqdktm_SAl|c!;S3tX)!3AB!S{NR+=y<%Qx#?kHS559%${ZlKG3#i$I32nY zS=BwrD!w5&!(KzR7aWgh8dj8?%c2MW)B0ra`<=uK^g2TC_Eb|{%bK2lNNZX~-{{r! z%L6=$_BsVLQXvC|Y)$8xDh}ga;)oGKv%HP^t`k?ZYs&%6ySAj)7`k%j7vM@;1BHO7uZtj%y!2+VRhzZl)O|hMZ3Jug(&4BMVlGG&d`c($_ft{RIinYvd12 zOY)jr4U|8m!qy0a-esF4f!F~QY3mRXZ`5};RB7eSWOj(Q&t!Fm4mn}%2zf`ptHcg6 z#Gd4Fb*p589P1qJg9`*DM687WTw?djA+}Erv6l+s{|m7ZBhpJfN%d4>eS@LN6Y2c> zgsVS1pj<^fDnExZn(wRa;fjb+I4p!6n0uZ_(EoV{Aw_ys$M>?5sNIgQD^TiR$p;yn z{XLw2G&qZ4R(o1-z5p_CPT&zZFB8{6SjF6n&>a6WTEDC4 zD?Qm&#e#&c{-hFQl-&;{?NNStnvR{`p7qs6d4r{#Sei`9LGSu@S(AK;>$Ek}(D;G} z_-6p&D1yQ=CV57lmetv#&044&l0)V7uEzgolf2mo`V7w``)TF=vqVLXZ#M)Y>ZL~1 z-x&3EbL*pNi{7B3MD?7uqSyoWE$v5H-qubz`7@juX%}fxSK2z6E<7M@3H+~7voO+* zi&pn!t*xZpj`hVxh>v?!PtmG|#&%QPfBO}xS63^zJN6gY1_RKw_Y()`?iL`zyC<6pdY_m#rx`qWXl_`lV`DB z^LQ6H_*!lX0xu5Puj`Ov+vyfL?%HeiFNhSTtpwbra1SFHbqrxR^a@|AMY^Gkv^C`( zURivHb}zqvozqCa!gp3Yww+h!*3w$paQ4`~(zLnn2A^PUbw0=F+~_`Dqfx8rMe zsX0y}(uNhigXd0IZkN9n`4pQwK{$_Dw~Hl7rnvMji9s?@%+^%!x`(Qep|=F#lEBa< z3~8&AC`aSdK;s&<`!#7R1PX~m2|8qac@_cUMwvE}r9uxY5Gz;53y{X<%!0|om^DXf zV5Puw`c9(W$Z;}}9ka&hE3&_%8AJ;1NTihmPwra+hRDGe*KaLiXfKxLA)tCKWRZK7$8 z4#a;&=BEd>#9s)|Xcl881xf!UG-=9p6OBiG_vR3`g%Q0nzD>%#RjH7axq&ii>s?b(&zy=L_X>P01si+@THS3?(5F!9Fs=``~|^1epi|10}`81DxS z`27#GgcGUY9WLm-PW@4S{C|Ui zmockZdkvh~G+`BMIG)-@s@$wcymt@(A!BJzfF%BPwx)+clj!?RhL0QM{m$(B26=zL zx0&~K@_u*r{bV7-@lV>V)0<_R5?@UaiHfUH!yM=l-K_|s2Pm{v26VN|S^5Y4ER0?Z zqsXx+?gz*f@c1ApG2EZ5WznzH7~h*$%w?yo-LRBX&Fhb-Y}&{7m1;JeDn$8aBW>oj zLJUjOq}R@%hxXNp2Be4e?~6(jL8VsHGu`6{P!?NipWIdfeSMD1ts3R^qp0Mjw`V!* z9zVz{a~JdiR0xn^ZQ@VD+O0OM#0F4}pi3vjIaQzn%F~-i4gu!sJMvDU-fIVt1M6edR7HIW*tE7B7x6FUY+3c72~) zA}M!KCIcMbV>@$S+$nd_UHPp?FS)yXRlyMb2%qx@r4J12OUx2C| z@?`$!5dePacdi{)tFk(XvfczVPdk+S2zmtlM@o{5{tOTO0|xzKHFH6KrlN23(2vNX zuWu9m-7fm;a?y|U&<73r`V9JS5meeb+(SP)i+RI~(-F zGU#7I$!ROaOqBZHOS9D=wpOjwe3tesxm5_$+WGD^(l5t|&X-6YoTX z#z}pOuCg9_v)rkZ@^6({==;vf5!AJY6JK($l9%tIA5z_INSr=4LsR#7gI8uJWzgLm z)t&S{i;zRw+8%ScZ&L1k(&|4rNx08PU#^z7A(2v&w2$i^zurTChe5xuTArXEujp^~ z&_}cA=e0(k*Nj}@?oDCWeMt}S`JYs{o#VHGEBTpL==*sG!gW^2y0DBFPV&qMH!fn% zJbzw=Eil27MZnN?XTy=pW6ZKeRRa zJXOj>o%N!VVO>Of%e~5cVf;BTC2v9k^haH(PDPs&LG1k66|Mg#t`g}{84Y!h|J{&V zn7nUbTlTYv0un(yH9Avu_xJ*@%vpxTKLTVThy#_y;n1m#bD^?WC=xuSHTpb(DvNL2 zn4wKreEJ?`u}gfVu=u1_=ezRD*48tbXC_GMvI7xM?jHZjE33)ns^i2(3UrjJ6c|)0LQGA{3Px?McHH(jkdgYcSy^-VDI=5 z5S9EzDffk&lszo*B6tW58d_N2Ik6G>$W|p+!iFpE?(yD+=e?61TI-79_ar?Lyd*xr z*-5?@+-l2xRSNv!Ypmm2NddNhOA(jt13b3xhM_FmgB@;_?XJT1`_Q0l|3v$N?MnqX zW_=)RKS+F@<8ZqEQP`f^Gd@7r?o2^#E#(lGY01Afy!A|;1bKA&YzR|awl?vZ!6Z(j zd;DY%rUQzE=8M!YHj^6IosdGz)<^i#TqgK*0z|$VyFfLdYka!Lf8!x|>o`U5JE2(? zat-otfD6_25-vQZ`J(L^Zk1_^DiR4QR;D|12h+C-aLoE0KZ&O`DBK}5R505Oy|z|& z!9K|gs4i_yZUh+0YTT6^-Q#B(daPuXp{HGzo|m~)dft+T2cSdX4x zaR)t52yo1rPvOLY9z7->Xh<4mNV1Y|^as-M-69Ma2}G~}t#O!8k*)D$Znehc(s2KU zdQ<{`=Z+c=65yEiYYHd+tZ_IJaPy2RgfU)?lc>>+|AGw^iZ*H%QWC1F-Lal91{%^% z79c*CS%A^f<3a3EJPEO-P$)XR#H_BWm@b6!c_N=Rb3sz&HTja_wG#K-!|$uV6?Jc@NIye04Ix@ZAnGXyUuNB z=Yu=A*ph}eU9sb^vtJZqs<$g!@@35*Xs`C_M$O3rkA)2Kuw0PEauitTXbT0q?0S(aU3&~=~+u?CumUEe3Wd``ceNs+8E>$6y^o+y%_#Rat; zgNVhWeM;&Bw&Q)SrR8KDnkb)%Dq7?$Yj_Y-si;mC zS-S1*1@5)I%@k#3+uQlvYI}S56BKm?>eu#mJ9o6Vp#mJUuB33{B#m`*Jx&vpRK_kS zPJVZ^n8eqx(o)OF;1-3c-7onXFwk|A8j(7|c3dh+iO+IL>h}(}O44)4Bk{>GE=dn@ z2T6+rIA*QjC-FFn5%*3PAR) z5wE%-rn*-JIA%=|2@EuL5O#fuC>du!-6(m#_#db``FY=r+j(U!B>@$W5;mWf8fZeu z{o=O^^kSfqNwYwDi%-?}Jj0U^DZ@}SRAQPyYv1#_v0WU7-Ui#6v zRa0HK&y<3e&C2*6vFBXRbFaEfSsY>A^-sabr-PoSV#e#|Ij%K62;$)4{irng8u?(_7jtXeM@KXMx!i_bep2N)ynY{neU?}A!Q4^vSX1*_ zeiA2Z&0Z&hn=^`)P4BwLA5{t_?UzjTk;s7V!LjKp)=FPNp1|}Kud|rk)fZQZA5VkW z7X9<%U=iC$J_;7k9&)vk?Fi(p+EojkSpiyYwo3^+tQ1C9BWA0v7`lAE z_70=H-htg9bNB9Qt9S6$R3A<>klQh^?A%*dZ8`=t`j+$wKOG04+b6JOf>vcmO^+d7 z-s`lqOxbqR9mI|BijymkluFv{4*qggwmbOxLwJ$+F1I_l1bIq#a6VGeSbJGkpO=ES z?e4&;`aq)f^4L$rDP;J^OK*(D6 z2CciNH2$xTFg)diAJyt=vYQ#?4e;`YfAZahZ>U<3^|)}(cVSxF?-2c zBa>&lwBm!M;v;%<|4c>*m``#Fv>0Qbs?Rk(s?mqOz0^3h|M?xQT;0)hd6wxA22A2s z-O<=>H2lfnS9f$ecQ}P10?fV=;xLXRJY`Jb4Y+QN>VT_P{9ja?j3GSvgxvE5%y1fC zz;_W=H7$!#CjJ0xu}(2}iO+@zJ~v5}0157(SF!=h^Mpm2neT~4b^cGhm5jgYX1RJv zDcik7JZB#+g^vt0-KU$ac(Z`XTe!t9{tQ#DcRrO%6~`g+EF+G=J-AhIT#i-}qu@%# zF_}BW@f|;i!v|2J_&6os%lyp0K?h=o#tV8t$XvK^1M=st6u2GIeh-MZ=cNWJ@Or44 zJ9|JTuP+t4e2)Ri@C=`(-Qpc`%SUz9(YYmwlT2-b<+ylP7fXe~Vg@0ynWQ&5$gD#6 zGIejj*A}$qTpf*%1R`< zx(W%^Ouw0l6Wv%U`Mkj&1HW-KlW{tHHw^U%taH2!T&cf`*Ua%SWjn>ksP#xjGqb*2 za-3i0<~Un5A74k%yN16Mxv1ecbCEUt0nc-*hJPnSCB`9HHT)Ze#F+070vxj%`AHm1 z(3s-~ZfH_Ja8$ekqLWSNAzVJ-9yrNKc@Ee*(@l8}_?kC4DNlOrV+B@Hp8eyestgHi zj2>)b(hKyy*QkA+B_{&d5l& zXMBoBG%WT@E9!3gqeNT_eZ7rUz zZ2pllXHzDA8}Px!MuJZNx~#XnRWW5FT%+Ovl@dYg8jn2~P?+_^69 z2Aa6KxGTBHadCCrs*C&e>$tJIdvPa=QTuR*i#uI_W7aT!5)+B)To>0po`4(@=w~M{ zL$KNH&Ht>LX|*Mv*pKUhisPSCRH(FRoM#(ym+UY8I&YT6xAGEcO|s1h_Pqvzas_k) zC`HKEq74=Db`{XsBINfGa(-(@8uj2qcm`jMv7tVBHgueeR-P-`U5fS`LHk=*|DZ8A zzB9TTAP;hMx1F-8e{`xi3VKSr9Va+Wb@hL-hhvrq+VOoY4{~rk>Ed|M!_h@?j29fg zuzMlwJeI-n!bZjMmpmL(TpSZT96c1r1i`To9OVC1BHA;sDv!6QWMc9@gMTRKGUET9 zDst)T;^J=S;qIllCkpO~ly5`)J1@!+|K*15A9$9F_OGB5(ft|1RCGrw+H(bMXC?VL zsB%rId%V9Pxi*>79?16x&}B$I;GAGg|+7ei@c<9^&pQ+`J1j`;!t9J7ijocM!;n@)mH6-i7Dr>?5ll*!4* zepI^}4z(E}KAG3-_I4@l;!#?olui~(FQ9xILj0vpDs!9NBqM-BDW0qMh1`n}|B7H# zi03HU^960dko6DH7$N%Bt0GqAk#>cP<1!D&WW_N>aIE`5%sm2U8QZypx0w3{$!B>- zb27xAuva~WIaHT8sMuB5 z9PUuqGe%*LQ#kQ45oAVTv#1+|O-=rrWzp`41`qj*ilvqBjVse`IC-^UdTMeZMcW|y z%&Ah(mzp!M21uKCHstC__!m8O1u1&kKRyS9>db?SDe6WIU+#k_Te$N(dd-->m=ULu6*0uok+sX(UIB|qJr+Znwj#aizV z`;6M7J6?}^^kWKjrZei_CiMf+b;dk~z0%S=egvh6-K#Hgv`(&*k^nC#nxj4R~tcXFLs~DbA92X0Ykiqda zP1@l2m1)HT`N_rcoQvZL5626NW18Ub861Df;P_Tr3hSTcK~Cp+iHqZW562S4F+*@H z-Ko4>n8ES5;pNUXEphaAag=yCRw<5%;FtrBv~`3^Rqg1g_(tCT*Qo4`9!l<8Qk7TrgNK0*Z3v?y@2}gY%QBMxV0RYQwwQ&@q=2+*;31Fua;q^mX4;D+bNu= zpcWb0Nnj@v`^T0wGwpX}>!DfvO|ZyV?+HYyBf{i6MVYkUOY|fyhl!hA@CAU=NUm&a zn0O}$5Zk>{Sf)BnCNQSBh8Ek=F(neBGAGZ3OYY=6TiuX(kO>!amALU3Q@!oZj`fLu z@IO?)AJymiyH?{(#3CQ>Io7f0oUyDsFs?{jo9X6Jf1kK0QMTI@PKm9rl)n74Ff*oDK3pv}Hc4e+rsDtovM||lip6}&X@=^) zU(0*lzf-a?fyN&Lf{AGs2U5dk2A+P0G^;xUpG~XDw8!D62#4A*`owqSFopWA@R$na zFtrN&t(fwys+DOD{2)R*o<=HeL#!*erC$eI;&HAvGVB}wR@G$&w!y_$BC<%(A%UK;rl;>MRd5s=rF2x1NKLG=y2vrDf1_=dedZm9+m4o z>o7P#-sk8r&|NU0!`0OM-*q@?aE>haP%C4Y!Ns>AfVQ+)Kn<<6SU~LVY4HUWQ)?|Q zhB49N3q~ki<3&`MoZH4I4oPlAB(j@^V;zoE5ZsHD!Y=yr&&{`tIv_)QpZNaZ@=_fb z;E1;Dp~^3qtT$1f+MBZRsU5%uY0sG{mws_>)iwVMF(f{soOS?PxT6Dj zP=I6B3<@V6{gsMCqW!+{J}p?0I@@C91X#hLcF2-35QIv`XE|ihLu~QLIBh9ao#T-) zh&#wwXvp|c$T$cxa(jrQ$=>yan$}%}k%iYsBp>gl#9V>(xSirBo=ko(1iCVTmXP5n}rNFU0Jx#Oz_l?pr0%o3Z<+xl0V_ zE%Mh0yl?zimA}b)X8Ba7Vg&Jon1LJ=kk^*nohI)j1(4f8_KhF^KVhuzA`DC47_BLm z@Aqewpy0bn2AB{0b?(g3+o8<+%H(vLy`?TE(cW@>V*HUgj1B&u$k+-Q`JPuc*=spx zLtA_e0}{WnT7z002bnYNyvLWMWA>%{o+43y53Tpmq^SP$8bUhOOpSC_5>s2Rb zk@cE&AsxXuE%9gZ05jp3-mc4HFK+mXs4ZKe4@5F_WsSRf!cUbIQ z0vxmMrf{O)-cqZseCQfKmnxFO7&pEL3KL0cWuJw7qF z7M~oz*e44;#$Hz`H5jB_0PK|E-Yb^mt?Kt6M3ab8PW4;F zo#-M6hmM*K!p6*{5Z(dKge3TkQQG7O4xf;Byo#1QH(_#711;T?w!*@Uk z`zo1|edBYLNUx(ZM7nDMa{Ou-BEj+p=Ew||-zk!`>qc^V1< zy5y;Tq)O8tZ-g5o&D8KbQF0D8P_Byi#!Vz9(|~TGKr)Hok?+O~;_WJee$xGyKvA}w;K;x&@3|+%DEGz@iFooXyxAW}iJzn9VyI zrOi_q%{G&*_hn?e#)`JKYiw(iS8tJP8}Qz8&^9V?_q%NMh7D>O2G5bk!tS~agF9fm z1(AKi&N@5h--vVP6Eb#v9etcA+&>05sdk{8$zw+c4CTI_uOIcZjMSm*Cv~O(=uxBo zml!nSuxCeYh6-*>9SYX6)Dr%R;e{ zuaWN_E^RQ-I0B=-AaHOaAVS@~(1@SwuT<+=#6VQHZVk9i~ciu!}> z!Eb@aQ6P~5vuvkpsC;c?G%!W>pkPt`ks;^xwDl8w>8Zd0_JtI-sk!Ml6w<2LYnT;WPmyCY^=aEsXB$R(p{}0)`f0ir+E`b4Avu`yUfi+z(@UgJG z(EWuu7Ijo4}f*T@OR9Gv^S*N4N{^r2*b2-T;W<7vwNBducE{tw&P{!Ci zsh0A(c~VNC-UTYP1-P}qasLqaEx?bt1C-Jc-#dyInP zhHU4Zv~?STU`IAJyEVG;_Kvjmwi@3S>GIFPy{`E?yxr)Z zo{f{j)PobT^*s8|($hT~zNodGt2jN8P0=qe4m&^C&e>o}rmYE3oUSOTl#Qa82j;G_ zypR))V*b8}i|-wViAo}!+5G-1#N0sVSwFsEUdfR9F|v*J!S>|SitO@L_4|^((hM*y zcVproC@?7wT(ahDNxe0~r%^N{fJ5bSR%dKvvF5urCT;>WHQ|Ds_A|P_%C)Mulw;B3 zup&D)q&U?n!NjpbF^E*UhoWKP$#bvk#eDoQD~Ivm3UL(VvK ze=!M!nVbXhi5OyzQ^}XN+1WN&7?MiGRBY#oYgH24>$@>85~g>&lY916!~77vk^FjY z5WI!p|4JvEwi-`Y!FS}ufJ@ZK<0SZgb-t&KOc{xXT`2k+eOj|bz)(kIm zxl>;FC>`&ergqPJcav+*L!&nJ-l>pWv@$O2Du-KRUo?$Nq-h{wZ5kM3u#kSH-1h-U zX(V_`IIr&|X_yje+$FlVVr6V-`@}ewJS1NbiIn?x0bXz0&WIxC^di~TIyP8jRX;$t z=aX7`%g0?imzLDC*W}D%cSBG?7;L1~ZHRQ1T@hbPP37NYU1co|wjym+AwILQ9|woZ zQHjlR5|SJ&p!;{TomgE78;d0d%Q#B+`qTaRB%`pAuhhJj=Y0dACurHST2Ifgkk77Y zdSlfwNvq5L=h{Q0w8-t$@Z!!7J1dGxY-f65q+{9E6s(-hn#hka&8+^pLH~z|{_*bU z-&XziFn$mTM`-S<4BaVtT^f7v-=WsMCC3!5c1_`AOkuuMfGNyUv!2PFF@>#S3g3x< z+>X3Ju4C5O(yhoQblXmo3#pK=pYl3V;SbewzJxkO?bgtCx$hH|_&fCn1Q*aNfsc(f z6PM{a2ln3+((&EpzK)Q=iHJt!JC_zl=q|;y6U#*tROW)_SVPdY0Z(}2igzw;ulOlv zYKs)99psdkLuxa~baP7$%+pq11Zym#ejib5|FW%yOOHmi77?-<%(}$>vRFk;q~&&y zQq~oaTU4x|mHxk%v+_cP&ip5kN?_r(gJzzr8YI|ZlZW-v>SP0lmqcdx`#{sPa%H~0 zF~ZNN-cHx{XH0$duImLC|5ZdTlRf>$s=j1iD^f41ANeR3+!qR`M6K>VmSeE(4Q3z9 z$ndZ;jN3PYyObIKHRkoxyjElRTa{dBkCZXd zY;(w$hq}dgIZ=|fNKo)KMmz(CSr{2_0n`&o7aX8x3j&>G~dg{byOZ;hTfbhoK zHroe7#}@F(JN}(cyENtb>zw}Gb}lNyiQ%#7t%rnTGm7PWxy*U3TO%cI%d;wAnWn=h zQE#ZA`|CYhJj#KStK_^^yZqm?SE5%tP}%EqI1@XOZ48yHwmNR)aHzn=wEtJZo

N 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 None: + pass + + @overload + def __call__(self, schema: Dict[str, Any], model_class: Type[BaseModel]) -> None: + pass + +else: + SchemaExtraCallable = Callable[..., None] + +__all__ = 'BaseConfig', 'ConfigDict', 'get_config', 'Extra', 'inherit_config', 'prepare_config' + + +class Extra(str, Enum): + allow = 'allow' + ignore = 'ignore' + forbid = 'forbid' + + +# https://github.com/cython/cython/issues/4003 +# Will be fixed with Cython 3 but still in alpha right now +if not compiled: + from typing_extensions import TypedDict + + class ConfigDict(TypedDict, total=False): + title: Optional[str] + anystr_lower: bool + anystr_strip_whitespace: bool + min_anystr_length: int + max_anystr_length: Optional[int] + validate_all: bool + extra: Extra + allow_mutation: bool + frozen: bool + allow_population_by_field_name: bool + use_enum_values: bool + fields: Dict[str, Union[str, Dict[str, str]]] + validate_assignment: bool + error_msg_templates: Dict[str, str] + arbitrary_types_allowed: bool + orm_mode: bool + getter_dict: Type[GetterDict] + alias_generator: Optional[Callable[[str], str]] + keep_untouched: Tuple[type, ...] + schema_extra: Union[Dict[str, object], 'SchemaExtraCallable'] + json_loads: Callable[[str], object] + json_dumps: AnyArgTCallable[str] + json_encoders: Dict[Type[object], AnyCallable] + underscore_attrs_are_private: bool + allow_inf_nan: bool + + # whether or not inherited models as fields should be reconstructed as base model + copy_on_model_validation: bool + # whether dataclass `__post_init__` should be run after validation + post_init_call: Literal['before_validation', 'after_validation'] + +else: + ConfigDict = dict # type: ignore + + +class BaseConfig: + title: Optional[str] = None + anystr_lower: bool = False + anystr_upper: bool = False + anystr_strip_whitespace: bool = False + min_anystr_length: int = 0 + max_anystr_length: Optional[int] = None + validate_all: bool = False + extra: Extra = Extra.ignore + allow_mutation: bool = True + frozen: bool = False + allow_population_by_field_name: bool = False + use_enum_values: bool = False + fields: Dict[str, Union[str, Dict[str, str]]] = {} + validate_assignment: bool = False + error_msg_templates: Dict[str, str] = {} + arbitrary_types_allowed: bool = False + orm_mode: bool = False + getter_dict: Type[GetterDict] = GetterDict + alias_generator: Optional[Callable[[str], str]] = None + keep_untouched: Tuple[type, ...] = () + schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {} + json_loads: Callable[[str], Any] = json.loads + json_dumps: Callable[..., str] = json.dumps + json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {} + underscore_attrs_are_private: bool = False + allow_inf_nan: bool = True + + # whether inherited models as fields should be reconstructed as base model, + # and whether such a copy should be shallow or deep + copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow' + + # whether `Union` should check all allowed types before even trying to coerce + smart_union: bool = False + # whether dataclass `__post_init__` should be run before or after validation + post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation' + + @classmethod + def get_field_info(cls, name: str) -> Dict[str, Any]: + """ + Get properties of FieldInfo from the `fields` property of the config class. + """ + + fields_value = cls.fields.get(name) + + if isinstance(fields_value, str): + field_info: Dict[str, Any] = {'alias': fields_value} + elif isinstance(fields_value, dict): + field_info = fields_value + else: + field_info = {} + + if 'alias' in field_info: + field_info.setdefault('alias_priority', 2) + + if field_info.get('alias_priority', 0) <= 1 and cls.alias_generator: + alias = cls.alias_generator(name) + if not isinstance(alias, str): + raise TypeError(f'Config.alias_generator must return str, not {alias.__class__}') + field_info.update(alias=alias, alias_priority=1) + return field_info + + @classmethod + def prepare_field(cls, field: 'ModelField') -> None: + """ + Optional hook to check or modify fields during model creation. + """ + pass + + +def get_config(config: Union[ConfigDict, Type[object], None]) -> Type[BaseConfig]: + if config is None: + return BaseConfig + + else: + config_dict = ( + config + if isinstance(config, dict) + else {k: getattr(config, k) for k in dir(config) if not k.startswith('__')} + ) + + class Config(BaseConfig): + ... + + for k, v in config_dict.items(): + setattr(Config, k, v) + return Config + + +def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType': + if not self_config: + base_classes: Tuple['ConfigType', ...] = (parent_config,) + elif self_config == parent_config: + base_classes = (self_config,) + else: + base_classes = self_config, parent_config + + namespace['json_encoders'] = { + **getattr(parent_config, 'json_encoders', {}), + **getattr(self_config, 'json_encoders', {}), + **namespace.get('json_encoders', {}), + } + + return type('Config', base_classes, namespace) + + +def prepare_config(config: Type[BaseConfig], cls_name: str) -> None: + if not isinstance(config.extra, Extra): + try: + config.extra = Extra(config.extra) + except ValueError: + raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"') diff --git a/libs/win/pydantic/dataclasses.cp37-win_amd64.pyd b/libs/win/pydantic/dataclasses.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..c672f8d600c29210a50543e50d477e3ac4f5e407 GIT binary patch literal 182272 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 NNz6`+$V)4K{|$(>CJ+Ds literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/dataclasses.py b/libs/win/pydantic/dataclasses.py new file mode 100644 index 00000000..68331127 --- /dev/null +++ b/libs/win/pydantic/dataclasses.py @@ -0,0 +1,479 @@ +""" +The main purpose is to enhance stdlib dataclasses by adding validation +A pydantic dataclass can be generated from scratch or from a stdlib one. + +Behind the scene, a pydantic dataclass is just like a regular one on which we attach +a `BaseModel` and magic methods to trigger the validation of the data. +`__init__` and `__post_init__` are hence overridden and have extra logic to be +able to validate input data. + +When a pydantic dataclass is generated from scratch, it's just a plain dataclass +with validation triggered at initialization + +The tricky part if for stdlib dataclasses that are converted after into pydantic ones e.g. + +```py +@dataclasses.dataclass +class M: + x: int + +ValidatedM = pydantic.dataclasses.dataclass(M) +``` + +We indeed still want to support equality, hashing, repr, ... as if it was the stdlib one! + +```py +assert isinstance(ValidatedM(x=1), M) +assert ValidatedM(x=1) == M(x=1) +``` + +This means we **don't want to create a new dataclass that inherits from it** +The trick is to create a wrapper around `M` that will act as a proxy to trigger +validation without altering default `M` behaviour. +""" +import sys +from contextlib import contextmanager +from functools import wraps +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + Generator, + Optional, + Set, + Type, + TypeVar, + Union, + overload, +) + +from typing_extensions import dataclass_transform + +from .class_validators import gather_all_validators +from .config import BaseConfig, ConfigDict, Extra, get_config +from .error_wrappers import ValidationError +from .errors import DataclassTypeError +from .fields import Field, FieldInfo, Required, Undefined +from .main import create_model, validate_model +from .utils import ClassAttribute + +if TYPE_CHECKING: + from .main import BaseModel + from .typing import CallableGenerator, NoArgAnyCallable + + DataclassT = TypeVar('DataclassT', bound='Dataclass') + + DataclassClassOrWrapper = Union[Type['Dataclass'], 'DataclassProxy'] + + class Dataclass: + # stdlib attributes + __dataclass_fields__: ClassVar[Dict[str, Any]] + __dataclass_params__: ClassVar[Any] # in reality `dataclasses._DataclassParams` + __post_init__: ClassVar[Callable[..., None]] + + # Added by pydantic + __pydantic_run_validation__: ClassVar[bool] + __post_init_post_parse__: ClassVar[Callable[..., None]] + __pydantic_initialised__: ClassVar[bool] + __pydantic_model__: ClassVar[Type[BaseModel]] + __pydantic_validate_values__: ClassVar[Callable[['Dataclass'], None]] + __pydantic_has_field_info_default__: ClassVar[bool] # whether a `pydantic.Field` is used as default value + + def __init__(self, *args: object, **kwargs: object) -> None: + pass + + @classmethod + def __get_validators__(cls: Type['Dataclass']) -> 'CallableGenerator': + pass + + @classmethod + def __validate__(cls: Type['DataclassT'], v: Any) -> 'DataclassT': + pass + + +__all__ = [ + 'dataclass', + 'set_validation', + 'create_pydantic_model_from_dataclass', + 'is_builtin_dataclass', + 'make_dataclass_validator', +] + +_T = TypeVar('_T') + +if sys.version_info >= (3, 10): + + @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @overload + def dataclass( + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + kw_only: bool = ..., + ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: + ... + + @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @overload + def dataclass( + _cls: Type[_T], + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + kw_only: bool = ..., + ) -> 'DataclassClassOrWrapper': + ... + +else: + + @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @overload + def dataclass( + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']: + ... + + @dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) + @overload + def dataclass( + _cls: Type[_T], + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + ) -> 'DataclassClassOrWrapper': + ... + + +@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) +def dataclass( + _cls: Optional[Type[_T]] = None, + *, + init: bool = True, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: Union[ConfigDict, Type[object], None] = None, + validate_on_init: Optional[bool] = None, + kw_only: bool = False, +) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']: + """ + Like the python standard lib dataclasses but with type validation. + The result is either a pydantic dataclass that will validate input data + or a wrapper that will trigger validation around a stdlib dataclass + to avoid modifying it directly + """ + the_config = get_config(config) + + def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper': + import dataclasses + + if is_builtin_dataclass(cls) and _extra_dc_args(_cls) == _extra_dc_args(_cls.__bases__[0]): # type: ignore + dc_cls_doc = '' + dc_cls = DataclassProxy(cls) + default_validate_on_init = False + else: + dc_cls_doc = cls.__doc__ or '' # needs to be done before generating dataclass + if sys.version_info >= (3, 10): + dc_cls = dataclasses.dataclass( + cls, + init=init, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen, + kw_only=kw_only, + ) + else: + dc_cls = dataclasses.dataclass( # type: ignore + cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen + ) + default_validate_on_init = True + + should_validate_on_init = default_validate_on_init if validate_on_init is None else validate_on_init + _add_pydantic_validation_attributes(cls, the_config, should_validate_on_init, dc_cls_doc) + dc_cls.__pydantic_model__.__try_update_forward_refs__(**{cls.__name__: cls}) + return dc_cls + + if _cls is None: + return wrap + + return wrap(_cls) + + +@contextmanager +def set_validation(cls: Type['DataclassT'], value: bool) -> Generator[Type['DataclassT'], None, None]: + original_run_validation = cls.__pydantic_run_validation__ + try: + cls.__pydantic_run_validation__ = value + yield cls + finally: + cls.__pydantic_run_validation__ = original_run_validation + + +class DataclassProxy: + __slots__ = '__dataclass__' + + def __init__(self, dc_cls: Type['Dataclass']) -> None: + object.__setattr__(self, '__dataclass__', dc_cls) + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + with set_validation(self.__dataclass__, True): + return self.__dataclass__(*args, **kwargs) + + def __getattr__(self, name: str) -> Any: + return getattr(self.__dataclass__, name) + + def __instancecheck__(self, instance: Any) -> bool: + return isinstance(instance, self.__dataclass__) + + +def _add_pydantic_validation_attributes( # noqa: C901 (ignore complexity) + dc_cls: Type['Dataclass'], + config: Type[BaseConfig], + validate_on_init: bool, + dc_cls_doc: str, +) -> None: + """ + We need to replace the right method. If no `__post_init__` has been set in the stdlib dataclass + it won't even exist (code is generated on the fly by `dataclasses`) + By default, we run validation after `__init__` or `__post_init__` if defined + """ + init = dc_cls.__init__ + + @wraps(init) + def handle_extra_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + if config.extra == Extra.ignore: + init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__}) + + elif config.extra == Extra.allow: + for k, v in kwargs.items(): + self.__dict__.setdefault(k, v) + init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__}) + + else: + init(self, *args, **kwargs) + + if hasattr(dc_cls, '__post_init__'): + post_init = dc_cls.__post_init__ + + @wraps(post_init) + def new_post_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + if config.post_init_call == 'before_validation': + post_init(self, *args, **kwargs) + + if self.__class__.__pydantic_run_validation__: + self.__pydantic_validate_values__() + if hasattr(self, '__post_init_post_parse__'): + self.__post_init_post_parse__(*args, **kwargs) + + if config.post_init_call == 'after_validation': + post_init(self, *args, **kwargs) + + setattr(dc_cls, '__init__', handle_extra_init) + setattr(dc_cls, '__post_init__', new_post_init) + + else: + + @wraps(init) + def new_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None: + handle_extra_init(self, *args, **kwargs) + + if self.__class__.__pydantic_run_validation__: + self.__pydantic_validate_values__() + + if hasattr(self, '__post_init_post_parse__'): + # We need to find again the initvars. To do that we use `__dataclass_fields__` instead of + # public method `dataclasses.fields` + import dataclasses + + # get all initvars and their default values + initvars_and_values: Dict[str, Any] = {} + for i, f in enumerate(self.__class__.__dataclass_fields__.values()): + if f._field_type is dataclasses._FIELD_INITVAR: # type: ignore[attr-defined] + try: + # set arg value by default + initvars_and_values[f.name] = args[i] + except IndexError: + initvars_and_values[f.name] = kwargs.get(f.name, f.default) + + self.__post_init_post_parse__(**initvars_and_values) + + setattr(dc_cls, '__init__', new_init) + + setattr(dc_cls, '__pydantic_run_validation__', ClassAttribute('__pydantic_run_validation__', validate_on_init)) + setattr(dc_cls, '__pydantic_initialised__', False) + setattr(dc_cls, '__pydantic_model__', create_pydantic_model_from_dataclass(dc_cls, config, dc_cls_doc)) + setattr(dc_cls, '__pydantic_validate_values__', _dataclass_validate_values) + setattr(dc_cls, '__validate__', classmethod(_validate_dataclass)) + setattr(dc_cls, '__get_validators__', classmethod(_get_validators)) + + if dc_cls.__pydantic_model__.__config__.validate_assignment and not dc_cls.__dataclass_params__.frozen: + setattr(dc_cls, '__setattr__', _dataclass_validate_assignment_setattr) + + +def _get_validators(cls: 'DataclassClassOrWrapper') -> 'CallableGenerator': + yield cls.__validate__ + + +def _validate_dataclass(cls: Type['DataclassT'], v: Any) -> 'DataclassT': + with set_validation(cls, True): + if isinstance(v, cls): + v.__pydantic_validate_values__() + return v + elif isinstance(v, (list, tuple)): + return cls(*v) + elif isinstance(v, dict): + return cls(**v) + else: + raise DataclassTypeError(class_name=cls.__name__) + + +def create_pydantic_model_from_dataclass( + dc_cls: Type['Dataclass'], + config: Type[Any] = BaseConfig, + dc_cls_doc: Optional[str] = None, +) -> Type['BaseModel']: + import dataclasses + + field_definitions: Dict[str, Any] = {} + for field in dataclasses.fields(dc_cls): + default: Any = Undefined + default_factory: Optional['NoArgAnyCallable'] = None + field_info: FieldInfo + + if field.default is not dataclasses.MISSING: + default = field.default + elif field.default_factory is not dataclasses.MISSING: + default_factory = field.default_factory + else: + default = Required + + if isinstance(default, FieldInfo): + field_info = default + dc_cls.__pydantic_has_field_info_default__ = True + else: + field_info = Field(default=default, default_factory=default_factory, **field.metadata) + + field_definitions[field.name] = (field.type, field_info) + + validators = gather_all_validators(dc_cls) + model: Type['BaseModel'] = create_model( + dc_cls.__name__, + __config__=config, + __module__=dc_cls.__module__, + __validators__=validators, + __cls_kwargs__={'__resolve_forward_refs__': False}, + **field_definitions, + ) + model.__doc__ = dc_cls_doc if dc_cls_doc is not None else dc_cls.__doc__ or '' + return model + + +def _dataclass_validate_values(self: 'Dataclass') -> None: + # validation errors can occur if this function is called twice on an already initialised dataclass. + # for example if Extra.forbid is enabled, it would consider __pydantic_initialised__ an invalid extra property + if getattr(self, '__pydantic_initialised__'): + return + if getattr(self, '__pydantic_has_field_info_default__', False): + # We need to remove `FieldInfo` values since they are not valid as input + # It's ok to do that because they are obviously the default values! + input_data = {k: v for k, v in self.__dict__.items() if not isinstance(v, FieldInfo)} + else: + input_data = self.__dict__ + d, _, validation_error = validate_model(self.__pydantic_model__, input_data, cls=self.__class__) + if validation_error: + raise validation_error + self.__dict__.update(d) + object.__setattr__(self, '__pydantic_initialised__', True) + + +def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value: Any) -> None: + if self.__pydantic_initialised__: + d = dict(self.__dict__) + d.pop(name, None) + known_field = self.__pydantic_model__.__fields__.get(name, None) + if known_field: + value, error_ = known_field.validate(value, d, loc=name, cls=self.__class__) + if error_: + raise ValidationError([error_], self.__class__) + + object.__setattr__(self, name, value) + + +def _extra_dc_args(cls: Type[Any]) -> Set[str]: + return { + x + for x in dir(cls) + if x not in getattr(cls, '__dataclass_fields__', {}) and not (x.startswith('__') and x.endswith('__')) + } + + +def is_builtin_dataclass(_cls: Type[Any]) -> bool: + """ + Whether a class is a stdlib dataclass + (useful to discriminated a pydantic dataclass that is actually a wrapper around a stdlib dataclass) + + we check that + - `_cls` is a dataclass + - `_cls` is not a processed pydantic dataclass (with a basemodel attached) + - `_cls` is not a pydantic dataclass inheriting directly from a stdlib dataclass + e.g. + ``` + @dataclasses.dataclass + class A: + x: int + + @pydantic.dataclasses.dataclass + class B(A): + y: int + ``` + In this case, when we first check `B`, we make an extra check and look at the annotations ('y'), + which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x') + """ + import dataclasses + + return ( + dataclasses.is_dataclass(_cls) + and not hasattr(_cls, '__pydantic_model__') + and set(_cls.__dataclass_fields__).issuperset(set(getattr(_cls, '__annotations__', {}))) + ) + + +def make_dataclass_validator(dc_cls: Type['Dataclass'], config: Type[BaseConfig]) -> 'CallableGenerator': + """ + Create a pydantic.dataclass from a builtin dataclass to add type validation + and yield the validators + It retrieves the parameters of the dataclass and forwards them to the newly created dataclass + """ + yield from _get_validators(dataclass(dc_cls, config=config, validate_on_init=False)) diff --git a/libs/win/pydantic/datetime_parse.cp37-win_amd64.pyd b/libs/win/pydantic/datetime_parse.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..6f98d735f9038f8313342bbe74080a32efb20ca5 GIT binary patch literal 96256 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/datetime_parse.py b/libs/win/pydantic/datetime_parse.py new file mode 100644 index 00000000..cfd54593 --- /dev/null +++ b/libs/win/pydantic/datetime_parse.py @@ -0,0 +1,248 @@ +""" +Functions to parse datetime objects. + +We're using regular expressions rather than time.strptime because: +- They provide both validation and parsing. +- They're more flexible for datetimes. +- The date/datetime/time constructors produce friendlier error messages. + +Stolen from https://raw.githubusercontent.com/django/django/main/django/utils/dateparse.py at +9718fa2e8abe430c3526a9278dd976443d4ae3c6 + +Changed to: +* use standard python datetime types not django.utils.timezone +* raise ValueError when regex doesn't match rather than returning None +* support parsing unix timestamps for dates and datetimes +""" +import re +from datetime import date, datetime, time, timedelta, timezone +from typing import Dict, Optional, Type, Union + +from . import errors + +date_expr = r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})' +time_expr = ( + r'(?P\d{1,2}):(?P\d{1,2})' + r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?' + r'(?PZ|[+-]\d{2}(?::?\d{2})?)?$' +) + +date_re = re.compile(f'{date_expr}$') +time_re = re.compile(time_expr) +datetime_re = re.compile(f'{date_expr}[T ]{time_expr}') + +standard_duration_re = re.compile( + r'^' + r'(?:(?P-?\d+) (days?, )?)?' + r'((?:(?P-?\d+):)(?=\d+:\d+))?' + r'(?:(?P-?\d+):)?' + r'(?P-?\d+)' + r'(?:\.(?P\d{1,6})\d{0,6})?' + r'$' +) + +# Support the sections of ISO 8601 date representation that are accepted by timedelta +iso8601_duration_re = re.compile( + r'^(?P[-+]?)' + r'P' + r'(?:(?P\d+(.\d+)?)D)?' + r'(?:T' + r'(?:(?P\d+(.\d+)?)H)?' + r'(?:(?P\d+(.\d+)?)M)?' + r'(?:(?P\d+(.\d+)?)S)?' + r')?' + r'$' +) + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) +StrBytesIntFloat = Union[str, bytes, int, float] + + +def get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f'invalid type; expected {native_expected_type}, string, bytes, int or float') + + +def from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str], error: Type[Exception]) -> Union[None, int, timezone]: + if value == 'Z': + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == '-': + offset = -offset + try: + return timezone(timedelta(minutes=offset)) + except ValueError: + raise error() + else: + return None + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = get_numeric(value, 'date') + if number is not None: + return from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + match = date_re.match(value) # type: ignore + if match is None: + raise errors.DateError() + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise errors.DateError() + + +def parse_time(value: Union[time, StrBytesIntFloat]) -> time: + """ + Parse a time/string and return a datetime.time. + + Raise ValueError if the input is well formatted but not a valid time. + Raise ValueError if the input isn't well formatted, in particular if it contains an offset. + """ + if isinstance(value, time): + return value + + number = get_numeric(value, 'time') + if number is not None: + if number >= 86400: + # doesn't make sense since the time time loop back around to 0 + raise errors.TimeError() + return (datetime.min + timedelta(seconds=number)).time() + + if isinstance(value, bytes): + value = value.decode() + + match = time_re.match(value) # type: ignore + if match is None: + raise errors.TimeError() + + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + + tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.TimeError) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_['tzinfo'] = tzinfo + + try: + return time(**kw_) # type: ignore + except ValueError: + raise errors.TimeError() + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = get_numeric(value, 'datetime') + if number is not None: + return from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + match = datetime_re.match(value) # type: ignore + if match is None: + raise errors.DateTimeError() + + kw = match.groupdict() + if kw['microsecond']: + kw['microsecond'] = kw['microsecond'].ljust(6, '0') + + tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.DateTimeError) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_['tzinfo'] = tzinfo + + try: + return datetime(**kw_) # type: ignore + except ValueError: + raise errors.DateTimeError() + + +def parse_duration(value: StrBytesIntFloat) -> timedelta: + """ + Parse a duration int/float/string and return a datetime.timedelta. + + The preferred format for durations in Django is '%d %H:%M:%S.%f'. + + Also supports ISO 8601 representation. + """ + if isinstance(value, timedelta): + return value + + if isinstance(value, (int, float)): + # below code requires a string + value = f'{value:f}' + elif isinstance(value, bytes): + value = value.decode() + + try: + match = standard_duration_re.match(value) or iso8601_duration_re.match(value) + except TypeError: + raise TypeError('invalid type; expected timedelta, string, bytes, int or float') + + if not match: + raise errors.DurationError() + + kw = match.groupdict() + sign = -1 if kw.pop('sign', '+') == '-' else 1 + if kw.get('microseconds'): + kw['microseconds'] = kw['microseconds'].ljust(6, '0') + + if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'): + kw['microseconds'] = '-' + kw['microseconds'] + + kw_ = {k: float(v) for k, v in kw.items() if v is not None} + + return sign * timedelta(**kw_) diff --git a/libs/win/pydantic/decorator.cp37-win_amd64.pyd b/libs/win/pydantic/decorator.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..ed58a1342df15f5333eb053ba1219136a8c7619f GIT binary patch literal 134144 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

IGRXYAV}e^hk$0K+=hW@PXN>UoMEGCJ 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 F{|2O2{}BKH literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/decorator.py b/libs/win/pydantic/decorator.py new file mode 100644 index 00000000..089aab65 --- /dev/null +++ b/libs/win/pydantic/decorator.py @@ -0,0 +1,264 @@ +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload + +from . import validator +from .config import Extra +from .errors import ConfigError +from .main import BaseModel, create_model +from .typing import get_all_type_hints +from .utils import to_camel + +__all__ = ('validate_arguments',) + +if TYPE_CHECKING: + from .typing import AnyCallable + + AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable) + ConfigType = Union[None, Type[Any], Dict[str, Any]] + + +@overload +def validate_arguments(func: None = None, *, config: 'ConfigType' = None) -> Callable[['AnyCallableT'], 'AnyCallableT']: + ... + + +@overload +def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT': + ... + + +def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any: + """ + Decorator to validate the arguments passed to a function. + """ + + def validate(_func: 'AnyCallable') -> 'AnyCallable': + vd = ValidatedFunction(_func, config) + + @wraps(_func) + def wrapper_function(*args: Any, **kwargs: Any) -> Any: + return vd.call(*args, **kwargs) + + wrapper_function.vd = vd # type: ignore + wrapper_function.validate = vd.init_model_instance # type: ignore + wrapper_function.raw_function = vd.raw_function # type: ignore + wrapper_function.model = vd.model # type: ignore + return wrapper_function + + if func: + return validate(func) + else: + return validate + + +ALT_V_ARGS = 'v__args' +ALT_V_KWARGS = 'v__kwargs' +V_POSITIONAL_ONLY_NAME = 'v__positional_only' +V_DUPLICATE_KWARGS = 'v__duplicate_kwargs' + + +class ValidatedFunction: + def __init__(self, function: 'AnyCallableT', config: 'ConfigType'): # noqa C901 + from inspect import Parameter, signature + + parameters: Mapping[str, Parameter] = signature(function).parameters + + if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}: + raise ConfigError( + f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" ' + f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator' + ) + + self.raw_function = function + self.arg_mapping: Dict[int, str] = {} + self.positional_only_args = set() + self.v_args_name = 'args' + self.v_kwargs_name = 'kwargs' + + type_hints = get_all_type_hints(function) + takes_args = False + takes_kwargs = False + fields: Dict[str, Tuple[Any, Any]] = {} + for i, (name, p) in enumerate(parameters.items()): + if p.annotation is p.empty: + annotation = Any + else: + annotation = type_hints[name] + + default = ... if p.default is p.empty else p.default + if p.kind == Parameter.POSITIONAL_ONLY: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_POSITIONAL_ONLY_NAME] = List[str], None + self.positional_only_args.add(name) + elif p.kind == Parameter.POSITIONAL_OR_KEYWORD: + self.arg_mapping[i] = name + fields[name] = annotation, default + fields[V_DUPLICATE_KWARGS] = List[str], None + elif p.kind == Parameter.KEYWORD_ONLY: + fields[name] = annotation, default + elif p.kind == Parameter.VAR_POSITIONAL: + self.v_args_name = name + fields[name] = Tuple[annotation, ...], None + takes_args = True + else: + assert p.kind == Parameter.VAR_KEYWORD, p.kind + self.v_kwargs_name = name + fields[name] = Dict[str, annotation], None # type: ignore + takes_kwargs = True + + # these checks avoid a clash between "args" and a field with that name + if not takes_args and self.v_args_name in fields: + self.v_args_name = ALT_V_ARGS + + # same with "kwargs" + if not takes_kwargs and self.v_kwargs_name in fields: + self.v_kwargs_name = ALT_V_KWARGS + + if not takes_args: + # we add the field so validation below can raise the correct exception + fields[self.v_args_name] = List[Any], None + + if not takes_kwargs: + # same with kwargs + fields[self.v_kwargs_name] = Dict[Any, Any], None + + self.create_model(fields, takes_args, takes_kwargs, config) + + def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel: + values = self.build_values(args, kwargs) + return self.model(**values) + + def call(self, *args: Any, **kwargs: Any) -> Any: + m = self.init_model_instance(*args, **kwargs) + return self.execute(m) + + def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[str, Any]: + values: Dict[str, Any] = {} + if args: + arg_iter = enumerate(args) + while True: + try: + i, a = next(arg_iter) + except StopIteration: + break + arg_name = self.arg_mapping.get(i) + if arg_name is not None: + values[arg_name] = a + else: + values[self.v_args_name] = [a] + [a for _, a in arg_iter] + break + + var_kwargs: Dict[str, Any] = {} + wrong_positional_args = [] + duplicate_kwargs = [] + fields_alias = [ + field.alias + for name, field in self.model.__fields__.items() + if name not in (self.v_args_name, self.v_kwargs_name) + ] + non_var_fields = set(self.model.__fields__) - {self.v_args_name, self.v_kwargs_name} + for k, v in kwargs.items(): + if k in non_var_fields or k in fields_alias: + if k in self.positional_only_args: + wrong_positional_args.append(k) + if k in values: + duplicate_kwargs.append(k) + values[k] = v + else: + var_kwargs[k] = v + + if var_kwargs: + values[self.v_kwargs_name] = var_kwargs + if wrong_positional_args: + values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args + if duplicate_kwargs: + values[V_DUPLICATE_KWARGS] = duplicate_kwargs + return values + + def execute(self, m: BaseModel) -> Any: + d = {k: v for k, v in m._iter() if k in m.__fields_set__ or m.__fields__[k].default_factory} + var_kwargs = d.pop(self.v_kwargs_name, {}) + + if self.v_args_name in d: + args_: List[Any] = [] + in_kwargs = False + kwargs = {} + for name, value in d.items(): + if in_kwargs: + kwargs[name] = value + elif name == self.v_args_name: + args_ += value + in_kwargs = True + else: + args_.append(value) + return self.raw_function(*args_, **kwargs, **var_kwargs) + elif self.positional_only_args: + args_ = [] + kwargs = {} + for name, value in d.items(): + if name in self.positional_only_args: + args_.append(value) + else: + kwargs[name] = value + return self.raw_function(*args_, **kwargs, **var_kwargs) + else: + return self.raw_function(**d, **var_kwargs) + + def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None: + pos_args = len(self.arg_mapping) + + class CustomConfig: + pass + + if not TYPE_CHECKING: # pragma: no branch + if isinstance(config, dict): + CustomConfig = type('Config', (), config) # noqa: F811 + elif config is not None: + CustomConfig = config # noqa: F811 + + if hasattr(CustomConfig, 'fields') or hasattr(CustomConfig, 'alias_generator'): + raise ConfigError( + 'Setting the "fields" and "alias_generator" property on custom Config for ' + '@validate_arguments is not yet supported, please remove.' + ) + + class DecoratorBaseModel(BaseModel): + @validator(self.v_args_name, check_fields=False, allow_reuse=True) + def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]: + if takes_args or v is None: + return v + + raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given') + + @validator(self.v_kwargs_name, check_fields=False, allow_reuse=True) + def check_kwargs(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: + if takes_kwargs or v is None: + return v + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v.keys())) + raise TypeError(f'unexpected keyword argument{plural}: {keys}') + + @validator(V_POSITIONAL_ONLY_NAME, check_fields=False, allow_reuse=True) + def check_positional_only(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}') + + @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True) + def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None: + if v is None: + return + + plural = '' if len(v) == 1 else 's' + keys = ', '.join(map(repr, v)) + raise TypeError(f'multiple values for argument{plural}: {keys}') + + class Config(CustomConfig): + extra = getattr(CustomConfig, 'extra', Extra.forbid) + + self.model = create_model(to_camel(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields) diff --git a/libs/win/pydantic/env_settings.cp37-win_amd64.pyd b/libs/win/pydantic/env_settings.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..1003ccc388fee0233aa9ce101126870184df90cf GIT binary patch literal 169472 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

#Q1`eeMDeO2P9-p*nqI=pCg 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 weRj}<9ptm4N``16`$r None: + # Uses something other than `self` the first arg to allow "self" as a settable attribute + super().__init__( + **__pydantic_self__._build_values( + values, + _env_file=_env_file, + _env_file_encoding=_env_file_encoding, + _env_nested_delimiter=_env_nested_delimiter, + _secrets_dir=_secrets_dir, + ) + ) + + def _build_values( + self, + init_kwargs: Dict[str, Any], + _env_file: Optional[DotenvType] = None, + _env_file_encoding: Optional[str] = None, + _env_nested_delimiter: Optional[str] = None, + _secrets_dir: Optional[StrPath] = None, + ) -> Dict[str, Any]: + # Configure built-in sources + init_settings = InitSettingsSource(init_kwargs=init_kwargs) + env_settings = EnvSettingsSource( + env_file=(_env_file if _env_file != env_file_sentinel else self.__config__.env_file), + env_file_encoding=( + _env_file_encoding if _env_file_encoding is not None else self.__config__.env_file_encoding + ), + env_nested_delimiter=( + _env_nested_delimiter if _env_nested_delimiter is not None else self.__config__.env_nested_delimiter + ), + env_prefix_len=len(self.__config__.env_prefix), + ) + file_secret_settings = SecretsSettingsSource(secrets_dir=_secrets_dir or self.__config__.secrets_dir) + # Provide a hook to set built-in sources priority and add / remove sources + sources = self.__config__.customise_sources( + init_settings=init_settings, env_settings=env_settings, file_secret_settings=file_secret_settings + ) + if sources: + return deep_update(*reversed([source(self) for source in sources])) + else: + # no one should mean to do this, but I think returning an empty dict is marginally preferable + # to an informative error and much better than a confusing error + return {} + + class Config(BaseConfig): + env_prefix: str = '' + env_file: Optional[DotenvType] = None + env_file_encoding: Optional[str] = None + env_nested_delimiter: Optional[str] = None + secrets_dir: Optional[StrPath] = None + validate_all: bool = True + extra: Extra = Extra.forbid + arbitrary_types_allowed: bool = True + case_sensitive: bool = False + + @classmethod + def prepare_field(cls, field: ModelField) -> None: + env_names: Union[List[str], AbstractSet[str]] + field_info_from_config = cls.get_field_info(field.name) + + env = field_info_from_config.get('env') or field.field_info.extra.get('env') + if env is None: + if field.has_alias: + warnings.warn( + 'aliases are no longer used by BaseSettings to define which environment variables to read. ' + 'Instead use the "env" field setting. ' + 'See https://pydantic-docs.helpmanual.io/usage/settings/#environment-variable-names', + FutureWarning, + ) + env_names = {cls.env_prefix + field.name} + elif isinstance(env, str): + env_names = {env} + elif isinstance(env, (set, frozenset)): + env_names = env + elif sequence_like(env): + env_names = list(env) + else: + raise TypeError(f'invalid field env: {env!r} ({display_as_type(env)}); should be string, list or set') + + if not cls.case_sensitive: + env_names = env_names.__class__(n.lower() for n in env_names) + field.field_info.extra['env_names'] = env_names + + @classmethod + def customise_sources( + cls, + init_settings: SettingsSourceCallable, + env_settings: SettingsSourceCallable, + file_secret_settings: SettingsSourceCallable, + ) -> Tuple[SettingsSourceCallable, ...]: + return init_settings, env_settings, file_secret_settings + + @classmethod + def parse_env_var(cls, field_name: str, raw_val: str) -> Any: + return cls.json_loads(raw_val) + + # populated by the metaclass using the Config class defined above, annotated here to help IDEs only + __config__: ClassVar[Type[Config]] + + +class InitSettingsSource: + __slots__ = ('init_kwargs',) + + def __init__(self, init_kwargs: Dict[str, Any]): + self.init_kwargs = init_kwargs + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: + return self.init_kwargs + + def __repr__(self) -> str: + return f'InitSettingsSource(init_kwargs={self.init_kwargs!r})' + + +class EnvSettingsSource: + __slots__ = ('env_file', 'env_file_encoding', 'env_nested_delimiter', 'env_prefix_len') + + def __init__( + self, + env_file: Optional[DotenvType], + env_file_encoding: Optional[str], + env_nested_delimiter: Optional[str] = None, + env_prefix_len: int = 0, + ): + self.env_file: Optional[DotenvType] = env_file + self.env_file_encoding: Optional[str] = env_file_encoding + self.env_nested_delimiter: Optional[str] = env_nested_delimiter + self.env_prefix_len: int = env_prefix_len + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: # noqa C901 + """ + Build environment variables suitable for passing to the Model. + """ + d: Dict[str, Any] = {} + + if settings.__config__.case_sensitive: + env_vars: Mapping[str, Optional[str]] = os.environ + else: + env_vars = {k.lower(): v for k, v in os.environ.items()} + + dotenv_vars = self._read_env_files(settings.__config__.case_sensitive) + if dotenv_vars: + env_vars = {**dotenv_vars, **env_vars} + + for field in settings.__fields__.values(): + env_val: Optional[str] = None + for env_name in field.field_info.extra['env_names']: + env_val = env_vars.get(env_name) + if env_val is not None: + break + + is_complex, allow_parse_failure = self.field_is_complex(field) + if is_complex: + if env_val is None: + # field is complex but no value found so far, try explode_env_vars + env_val_built = self.explode_env_vars(field, env_vars) + if env_val_built: + d[field.alias] = env_val_built + else: + # field is complex and there's a value, decode that as JSON, then add explode_env_vars + try: + env_val = settings.__config__.parse_env_var(field.name, env_val) + except ValueError as e: + if not allow_parse_failure: + raise SettingsError(f'error parsing env var "{env_name}"') from e + + if isinstance(env_val, dict): + d[field.alias] = deep_update(env_val, self.explode_env_vars(field, env_vars)) + else: + d[field.alias] = env_val + elif env_val is not None: + # simplest case, field is not complex, we only need to add the value if it was found + d[field.alias] = env_val + + return d + + def _read_env_files(self, case_sensitive: bool) -> Dict[str, Optional[str]]: + env_files = self.env_file + if env_files is None: + return {} + + if isinstance(env_files, (str, os.PathLike)): + env_files = [env_files] + + dotenv_vars = {} + for env_file in env_files: + env_path = Path(env_file).expanduser() + if env_path.is_file(): + dotenv_vars.update( + read_env_file(env_path, encoding=self.env_file_encoding, case_sensitive=case_sensitive) + ) + + return dotenv_vars + + def field_is_complex(self, field: ModelField) -> Tuple[bool, bool]: + """ + Find out if a field is complex, and if so whether JSON errors should be ignored + """ + if field.is_complex(): + allow_parse_failure = False + elif is_union(get_origin(field.type_)) and field.sub_fields and any(f.is_complex() for f in field.sub_fields): + allow_parse_failure = True + else: + return False, False + + return True, allow_parse_failure + + def explode_env_vars(self, field: ModelField, env_vars: Mapping[str, Optional[str]]) -> Dict[str, Any]: + """ + Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries. + + This is applied to a single field, hence filtering by env_var prefix. + """ + prefixes = [f'{env_name}{self.env_nested_delimiter}' for env_name in field.field_info.extra['env_names']] + result: Dict[str, Any] = {} + for env_name, env_val in env_vars.items(): + if not any(env_name.startswith(prefix) for prefix in prefixes): + continue + # we remove the prefix before splitting in case the prefix has characters in common with the delimiter + env_name_without_prefix = env_name[self.env_prefix_len :] + _, *keys, last_key = env_name_without_prefix.split(self.env_nested_delimiter) + env_var = result + for key in keys: + env_var = env_var.setdefault(key, {}) + env_var[last_key] = env_val + + return result + + def __repr__(self) -> str: + return ( + f'EnvSettingsSource(env_file={self.env_file!r}, env_file_encoding={self.env_file_encoding!r}, ' + f'env_nested_delimiter={self.env_nested_delimiter!r})' + ) + + +class SecretsSettingsSource: + __slots__ = ('secrets_dir',) + + def __init__(self, secrets_dir: Optional[StrPath]): + self.secrets_dir: Optional[StrPath] = secrets_dir + + def __call__(self, settings: BaseSettings) -> Dict[str, Any]: + """ + Build fields from "secrets" files. + """ + secrets: Dict[str, Optional[str]] = {} + + if self.secrets_dir is None: + return secrets + + secrets_path = Path(self.secrets_dir).expanduser() + + if not secrets_path.exists(): + warnings.warn(f'directory "{secrets_path}" does not exist') + return secrets + + if not secrets_path.is_dir(): + raise SettingsError(f'secrets_dir must reference a directory, not a {path_type(secrets_path)}') + + for field in settings.__fields__.values(): + for env_name in field.field_info.extra['env_names']: + path = find_case_path(secrets_path, env_name, settings.__config__.case_sensitive) + if not path: + # path does not exist, we curently don't return a warning for this + continue + + if path.is_file(): + secret_value = path.read_text().strip() + if field.is_complex(): + try: + secret_value = settings.__config__.parse_env_var(field.name, secret_value) + except ValueError as e: + raise SettingsError(f'error parsing env var "{env_name}"') from e + + secrets[field.alias] = secret_value + else: + warnings.warn( + f'attempted to load secret file "{path}" but found a {path_type(path)} instead.', + stacklevel=4, + ) + return secrets + + def __repr__(self) -> str: + return f'SecretsSettingsSource(secrets_dir={self.secrets_dir!r})' + + +def read_env_file( + file_path: StrPath, *, encoding: str = None, case_sensitive: bool = False +) -> Dict[str, Optional[str]]: + try: + from dotenv import dotenv_values + except ImportError as e: + raise ImportError('python-dotenv is not installed, run `pip install pydantic[dotenv]`') from e + + file_vars: Dict[str, Optional[str]] = dotenv_values(file_path, encoding=encoding or 'utf8') + if not case_sensitive: + return {k.lower(): v for k, v in file_vars.items()} + else: + return file_vars + + +def find_case_path(dir_path: Path, file_name: str, case_sensitive: bool) -> Optional[Path]: + """ + Find a file within path's directory matching filename, optionally ignoring case. + """ + for f in dir_path.iterdir(): + if f.name == file_name: + return f + elif not case_sensitive and f.name.lower() == file_name.lower(): + return f + return None diff --git a/libs/win/pydantic/error_wrappers.cp37-win_amd64.pyd b/libs/win/pydantic/error_wrappers.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..a9af44c7a141b9ca6833abaf3d5c89269eb7a16d GIT binary patch literal 117248 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
NMs&r6-3<%pv2*8@3 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 None: + self.exc = exc + self._loc = loc + + def loc_tuple(self) -> 'Loc': + if isinstance(self._loc, tuple): + return self._loc + else: + return (self._loc,) + + def __repr_args__(self) -> 'ReprArgs': + return [('exc', self.exc), ('loc', self.loc_tuple())] + + +# ErrorList is something like Union[List[Union[List[ErrorWrapper], ErrorWrapper]], ErrorWrapper] +# but recursive, therefore just use: +ErrorList = Union[Sequence[Any], ErrorWrapper] + + +class ValidationError(Representation, ValueError): + __slots__ = 'raw_errors', 'model', '_error_cache' + + def __init__(self, errors: Sequence[ErrorList], model: 'ModelOrDc') -> None: + self.raw_errors = errors + self.model = model + self._error_cache: Optional[List['ErrorDict']] = None + + def errors(self) -> List['ErrorDict']: + if self._error_cache is None: + try: + config = self.model.__config__ # type: ignore + except AttributeError: + config = self.model.__pydantic_model__.__config__ # type: ignore + self._error_cache = list(flatten_errors(self.raw_errors, config)) + return self._error_cache + + def json(self, *, indent: Union[None, int, str] = 2) -> str: + return json.dumps(self.errors(), indent=indent, default=pydantic_encoder) + + def __str__(self) -> str: + errors = self.errors() + no_errors = len(errors) + return ( + f'{no_errors} validation error{"" if no_errors == 1 else "s"} for {self.model.__name__}\n' + f'{display_errors(errors)}' + ) + + def __repr_args__(self) -> 'ReprArgs': + return [('model', self.model.__name__), ('errors', self.errors())] + + +def display_errors(errors: List['ErrorDict']) -> str: + return '\n'.join(f'{_display_error_loc(e)}\n {e["msg"]} ({_display_error_type_and_ctx(e)})' for e in errors) + + +def _display_error_loc(error: 'ErrorDict') -> str: + return ' -> '.join(str(e) for e in error['loc']) + + +def _display_error_type_and_ctx(error: 'ErrorDict') -> str: + t = 'type=' + error['type'] + ctx = error.get('ctx') + if ctx: + return t + ''.join(f'; {k}={v}' for k, v in ctx.items()) + else: + return t + + +def flatten_errors( + errors: Sequence[Any], config: Type['BaseConfig'], loc: Optional['Loc'] = None +) -> Generator['ErrorDict', None, None]: + for error in errors: + if isinstance(error, ErrorWrapper): + + if loc: + error_loc = loc + error.loc_tuple() + else: + error_loc = error.loc_tuple() + + if isinstance(error.exc, ValidationError): + yield from flatten_errors(error.exc.raw_errors, config, error_loc) + else: + yield error_dict(error.exc, config, error_loc) + elif isinstance(error, list): + yield from flatten_errors(error, config, loc=loc) + else: + raise RuntimeError(f'Unknown error object: {error}') + + +def error_dict(exc: Exception, config: Type['BaseConfig'], loc: 'Loc') -> 'ErrorDict': + type_ = get_exc_type(exc.__class__) + msg_template = config.error_msg_templates.get(type_) or getattr(exc, 'msg_template', None) + ctx = exc.__dict__ + if msg_template: + msg = msg_template.format(**ctx) + else: + msg = str(exc) + + d: 'ErrorDict' = {'loc': loc, 'msg': msg, 'type': type_} + + if ctx: + d['ctx'] = ctx + + return d + + +_EXC_TYPE_CACHE: Dict[Type[Exception], str] = {} + + +def get_exc_type(cls: Type[Exception]) -> str: + # slightly more efficient than using lru_cache since we don't need to worry about the cache filling up + try: + return _EXC_TYPE_CACHE[cls] + except KeyError: + r = _get_exc_type(cls) + _EXC_TYPE_CACHE[cls] = r + return r + + +def _get_exc_type(cls: Type[Exception]) -> str: + if issubclass(cls, AssertionError): + return 'assertion_error' + + base_name = 'type_error' if issubclass(cls, TypeError) else 'value_error' + if cls in (TypeError, ValueError): + # just TypeError or ValueError, no extra code + return base_name + + # if it's not a TypeError or ValueError, we just take the lowercase of the exception name + # no chaining or snake case logic, use "code" for more complex error types. + code = getattr(cls, 'code', None) or cls.__name__.replace('Error', '').lower() + return base_name + '.' + code diff --git a/libs/win/pydantic/errors.cp37-win_amd64.pyd b/libs/win/pydantic/errors.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..50d8e049ab81e0ef0403e6dd71c49134914481cc GIT binary patch literal 184320 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 z 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
Hf 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⋙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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/errors.py b/libs/win/pydantic/errors.py new file mode 100644 index 00000000..7bdafdd1 --- /dev/null +++ b/libs/win/pydantic/errors.py @@ -0,0 +1,646 @@ +from decimal import Decimal +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable, Sequence, Set, Tuple, Type, Union + +from .typing import display_as_type + +if TYPE_CHECKING: + from .typing import DictStrAny + +# explicitly state exports to avoid "from .errors import *" also importing Decimal, Path etc. +__all__ = ( + 'PydanticTypeError', + 'PydanticValueError', + 'ConfigError', + 'MissingError', + 'ExtraError', + 'NoneIsNotAllowedError', + 'NoneIsAllowedError', + 'WrongConstantError', + 'NotNoneError', + 'BoolError', + 'BytesError', + 'DictError', + 'EmailError', + 'UrlError', + 'UrlSchemeError', + 'UrlSchemePermittedError', + 'UrlUserInfoError', + 'UrlHostError', + 'UrlHostTldError', + 'UrlPortError', + 'UrlExtraError', + 'EnumError', + 'IntEnumError', + 'EnumMemberError', + 'IntegerError', + 'FloatError', + 'PathError', + 'PathNotExistsError', + 'PathNotAFileError', + 'PathNotADirectoryError', + 'PyObjectError', + 'SequenceError', + 'ListError', + 'SetError', + 'FrozenSetError', + 'TupleError', + 'TupleLengthError', + 'ListMinLengthError', + 'ListMaxLengthError', + 'ListUniqueItemsError', + 'SetMinLengthError', + 'SetMaxLengthError', + 'FrozenSetMinLengthError', + 'FrozenSetMaxLengthError', + 'AnyStrMinLengthError', + 'AnyStrMaxLengthError', + 'StrError', + 'StrRegexError', + 'NumberNotGtError', + 'NumberNotGeError', + 'NumberNotLtError', + 'NumberNotLeError', + 'NumberNotMultipleError', + 'DecimalError', + 'DecimalIsNotFiniteError', + 'DecimalMaxDigitsError', + 'DecimalMaxPlacesError', + 'DecimalWholeDigitsError', + 'DateTimeError', + 'DateError', + 'DateNotInThePastError', + 'DateNotInTheFutureError', + 'TimeError', + 'DurationError', + 'HashableError', + 'UUIDError', + 'UUIDVersionError', + 'ArbitraryTypeError', + 'ClassError', + 'SubclassError', + 'JsonError', + 'JsonTypeError', + 'PatternError', + 'DataclassTypeError', + 'CallableError', + 'IPvAnyAddressError', + 'IPvAnyInterfaceError', + 'IPvAnyNetworkError', + 'IPv4AddressError', + 'IPv6AddressError', + 'IPv4NetworkError', + 'IPv6NetworkError', + 'IPv4InterfaceError', + 'IPv6InterfaceError', + 'ColorError', + 'StrictBoolError', + 'NotDigitError', + 'LuhnValidationError', + 'InvalidLengthForBrand', + 'InvalidByteSize', + 'InvalidByteSizeUnit', + 'MissingDiscriminator', + 'InvalidDiscriminator', +) + + +def cls_kwargs(cls: Type['PydanticErrorMixin'], ctx: 'DictStrAny') -> 'PydanticErrorMixin': + """ + For built-in exceptions like ValueError or TypeError, we need to implement + __reduce__ to override the default behaviour (instead of __getstate__/__setstate__) + By default pickle protocol 2 calls `cls.__new__(cls, *args)`. + Since we only use kwargs, we need a little constructor to change that. + Note: the callable can't be a lambda as pickle looks in the namespace to find it + """ + return cls(**ctx) + + +class PydanticErrorMixin: + code: str + msg_template: str + + def __init__(self, **ctx: Any) -> None: + self.__dict__ = ctx + + def __str__(self) -> str: + return self.msg_template.format(**self.__dict__) + + def __reduce__(self) -> Tuple[Callable[..., 'PydanticErrorMixin'], Tuple[Type['PydanticErrorMixin'], 'DictStrAny']]: + return cls_kwargs, (self.__class__, self.__dict__) + + +class PydanticTypeError(PydanticErrorMixin, TypeError): + pass + + +class PydanticValueError(PydanticErrorMixin, ValueError): + pass + + +class ConfigError(RuntimeError): + pass + + +class MissingError(PydanticValueError): + msg_template = 'field required' + + +class ExtraError(PydanticValueError): + msg_template = 'extra fields not permitted' + + +class NoneIsNotAllowedError(PydanticTypeError): + code = 'none.not_allowed' + msg_template = 'none is not an allowed value' + + +class NoneIsAllowedError(PydanticTypeError): + code = 'none.allowed' + msg_template = 'value is not none' + + +class WrongConstantError(PydanticValueError): + code = 'const' + + def __str__(self) -> str: + permitted = ', '.join(repr(v) for v in self.permitted) # type: ignore + return f'unexpected value; permitted: {permitted}' + + +class NotNoneError(PydanticTypeError): + code = 'not_none' + msg_template = 'value is not None' + + +class BoolError(PydanticTypeError): + msg_template = 'value could not be parsed to a boolean' + + +class BytesError(PydanticTypeError): + msg_template = 'byte type expected' + + +class DictError(PydanticTypeError): + msg_template = 'value is not a valid dict' + + +class EmailError(PydanticValueError): + msg_template = 'value is not a valid email address' + + +class UrlError(PydanticValueError): + code = 'url' + + +class UrlSchemeError(UrlError): + code = 'url.scheme' + msg_template = 'invalid or missing URL scheme' + + +class UrlSchemePermittedError(UrlError): + code = 'url.scheme' + msg_template = 'URL scheme not permitted' + + def __init__(self, allowed_schemes: Set[str]): + super().__init__(allowed_schemes=allowed_schemes) + + +class UrlUserInfoError(UrlError): + code = 'url.userinfo' + msg_template = 'userinfo required in URL but missing' + + +class UrlHostError(UrlError): + code = 'url.host' + msg_template = 'URL host invalid' + + +class UrlHostTldError(UrlError): + code = 'url.host' + msg_template = 'URL host invalid, top level domain required' + + +class UrlPortError(UrlError): + code = 'url.port' + msg_template = 'URL port invalid, port cannot exceed 65535' + + +class UrlExtraError(UrlError): + code = 'url.extra' + msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}' + + +class EnumMemberError(PydanticTypeError): + code = 'enum' + + def __str__(self) -> str: + permitted = ', '.join(repr(v.value) for v in self.enum_values) # type: ignore + return f'value is not a valid enumeration member; permitted: {permitted}' + + +class IntegerError(PydanticTypeError): + msg_template = 'value is not a valid integer' + + +class FloatError(PydanticTypeError): + msg_template = 'value is not a valid float' + + +class PathError(PydanticTypeError): + msg_template = 'value is not a valid path' + + +class _PathValueError(PydanticValueError): + def __init__(self, *, path: Path) -> None: + super().__init__(path=str(path)) + + +class PathNotExistsError(_PathValueError): + code = 'path.not_exists' + msg_template = 'file or directory at path "{path}" does not exist' + + +class PathNotAFileError(_PathValueError): + code = 'path.not_a_file' + msg_template = 'path "{path}" does not point to a file' + + +class PathNotADirectoryError(_PathValueError): + code = 'path.not_a_directory' + msg_template = 'path "{path}" does not point to a directory' + + +class PyObjectError(PydanticTypeError): + msg_template = 'ensure this value contains valid import path or valid callable: {error_message}' + + +class SequenceError(PydanticTypeError): + msg_template = 'value is not a valid sequence' + + +class IterableError(PydanticTypeError): + msg_template = 'value is not a valid iterable' + + +class ListError(PydanticTypeError): + msg_template = 'value is not a valid list' + + +class SetError(PydanticTypeError): + msg_template = 'value is not a valid set' + + +class FrozenSetError(PydanticTypeError): + msg_template = 'value is not a valid frozenset' + + +class DequeError(PydanticTypeError): + msg_template = 'value is not a valid deque' + + +class TupleError(PydanticTypeError): + msg_template = 'value is not a valid tuple' + + +class TupleLengthError(PydanticValueError): + code = 'tuple.length' + msg_template = 'wrong tuple length {actual_length}, expected {expected_length}' + + def __init__(self, *, actual_length: int, expected_length: int) -> None: + super().__init__(actual_length=actual_length, expected_length=expected_length) + + +class ListMinLengthError(PydanticValueError): + code = 'list.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class ListMaxLengthError(PydanticValueError): + code = 'list.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class ListUniqueItemsError(PydanticValueError): + code = 'list.unique_items' + msg_template = 'the list has duplicated items' + + +class SetMinLengthError(PydanticValueError): + code = 'set.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class SetMaxLengthError(PydanticValueError): + code = 'set.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class FrozenSetMinLengthError(PydanticValueError): + code = 'frozenset.min_items' + msg_template = 'ensure this value has at least {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class FrozenSetMaxLengthError(PydanticValueError): + code = 'frozenset.max_items' + msg_template = 'ensure this value has at most {limit_value} items' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class AnyStrMinLengthError(PydanticValueError): + code = 'any_str.min_length' + msg_template = 'ensure this value has at least {limit_value} characters' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class AnyStrMaxLengthError(PydanticValueError): + code = 'any_str.max_length' + msg_template = 'ensure this value has at most {limit_value} characters' + + def __init__(self, *, limit_value: int) -> None: + super().__init__(limit_value=limit_value) + + +class StrError(PydanticTypeError): + msg_template = 'str type expected' + + +class StrRegexError(PydanticValueError): + code = 'str.regex' + msg_template = 'string does not match regex "{pattern}"' + + def __init__(self, *, pattern: str) -> None: + super().__init__(pattern=pattern) + + +class _NumberBoundError(PydanticValueError): + def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None: + super().__init__(limit_value=limit_value) + + +class NumberNotGtError(_NumberBoundError): + code = 'number.not_gt' + msg_template = 'ensure this value is greater than {limit_value}' + + +class NumberNotGeError(_NumberBoundError): + code = 'number.not_ge' + msg_template = 'ensure this value is greater than or equal to {limit_value}' + + +class NumberNotLtError(_NumberBoundError): + code = 'number.not_lt' + msg_template = 'ensure this value is less than {limit_value}' + + +class NumberNotLeError(_NumberBoundError): + code = 'number.not_le' + msg_template = 'ensure this value is less than or equal to {limit_value}' + + +class NumberNotFiniteError(PydanticValueError): + code = 'number.not_finite_number' + msg_template = 'ensure this value is a finite number' + + +class NumberNotMultipleError(PydanticValueError): + code = 'number.not_multiple' + msg_template = 'ensure this value is a multiple of {multiple_of}' + + def __init__(self, *, multiple_of: Union[int, float, Decimal]) -> None: + super().__init__(multiple_of=multiple_of) + + +class DecimalError(PydanticTypeError): + msg_template = 'value is not a valid decimal' + + +class DecimalIsNotFiniteError(PydanticValueError): + code = 'decimal.not_finite' + msg_template = 'value is not a valid decimal' + + +class DecimalMaxDigitsError(PydanticValueError): + code = 'decimal.max_digits' + msg_template = 'ensure that there are no more than {max_digits} digits in total' + + def __init__(self, *, max_digits: int) -> None: + super().__init__(max_digits=max_digits) + + +class DecimalMaxPlacesError(PydanticValueError): + code = 'decimal.max_places' + msg_template = 'ensure that there are no more than {decimal_places} decimal places' + + def __init__(self, *, decimal_places: int) -> None: + super().__init__(decimal_places=decimal_places) + + +class DecimalWholeDigitsError(PydanticValueError): + code = 'decimal.whole_digits' + msg_template = 'ensure that there are no more than {whole_digits} digits before the decimal point' + + def __init__(self, *, whole_digits: int) -> None: + super().__init__(whole_digits=whole_digits) + + +class DateTimeError(PydanticValueError): + msg_template = 'invalid datetime format' + + +class DateError(PydanticValueError): + msg_template = 'invalid date format' + + +class DateNotInThePastError(PydanticValueError): + code = 'date.not_in_the_past' + msg_template = 'date is not in the past' + + +class DateNotInTheFutureError(PydanticValueError): + code = 'date.not_in_the_future' + msg_template = 'date is not in the future' + + +class TimeError(PydanticValueError): + msg_template = 'invalid time format' + + +class DurationError(PydanticValueError): + msg_template = 'invalid duration format' + + +class HashableError(PydanticTypeError): + msg_template = 'value is not a valid hashable' + + +class UUIDError(PydanticTypeError): + msg_template = 'value is not a valid uuid' + + +class UUIDVersionError(PydanticValueError): + code = 'uuid.version' + msg_template = 'uuid version {required_version} expected' + + def __init__(self, *, required_version: int) -> None: + super().__init__(required_version=required_version) + + +class ArbitraryTypeError(PydanticTypeError): + code = 'arbitrary_type' + msg_template = 'instance of {expected_arbitrary_type} expected' + + def __init__(self, *, expected_arbitrary_type: Type[Any]) -> None: + super().__init__(expected_arbitrary_type=display_as_type(expected_arbitrary_type)) + + +class ClassError(PydanticTypeError): + code = 'class' + msg_template = 'a class is expected' + + +class SubclassError(PydanticTypeError): + code = 'subclass' + msg_template = 'subclass of {expected_class} expected' + + def __init__(self, *, expected_class: Type[Any]) -> None: + super().__init__(expected_class=display_as_type(expected_class)) + + +class JsonError(PydanticValueError): + msg_template = 'Invalid JSON' + + +class JsonTypeError(PydanticTypeError): + code = 'json' + msg_template = 'JSON object must be str, bytes or bytearray' + + +class PatternError(PydanticValueError): + code = 'regex_pattern' + msg_template = 'Invalid regular expression' + + +class DataclassTypeError(PydanticTypeError): + code = 'dataclass' + msg_template = 'instance of {class_name}, tuple or dict expected' + + +class CallableError(PydanticTypeError): + msg_template = '{value} is not callable' + + +class EnumError(PydanticTypeError): + code = 'enum_instance' + msg_template = '{value} is not a valid Enum instance' + + +class IntEnumError(PydanticTypeError): + code = 'int_enum_instance' + msg_template = '{value} is not a valid IntEnum instance' + + +class IPvAnyAddressError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 address' + + +class IPvAnyInterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 interface' + + +class IPvAnyNetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv4 or IPv6 network' + + +class IPv4AddressError(PydanticValueError): + msg_template = 'value is not a valid IPv4 address' + + +class IPv6AddressError(PydanticValueError): + msg_template = 'value is not a valid IPv6 address' + + +class IPv4NetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv4 network' + + +class IPv6NetworkError(PydanticValueError): + msg_template = 'value is not a valid IPv6 network' + + +class IPv4InterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv4 interface' + + +class IPv6InterfaceError(PydanticValueError): + msg_template = 'value is not a valid IPv6 interface' + + +class ColorError(PydanticValueError): + msg_template = 'value is not a valid color: {reason}' + + +class StrictBoolError(PydanticValueError): + msg_template = 'value is not a valid boolean' + + +class NotDigitError(PydanticValueError): + code = 'payment_card_number.digits' + msg_template = 'card number is not all digits' + + +class LuhnValidationError(PydanticValueError): + code = 'payment_card_number.luhn_check' + msg_template = 'card number is not luhn valid' + + +class InvalidLengthForBrand(PydanticValueError): + code = 'payment_card_number.invalid_length_for_brand' + msg_template = 'Length for a {brand} card must be {required_length}' + + +class InvalidByteSize(PydanticValueError): + msg_template = 'could not parse value and unit from byte string' + + +class InvalidByteSizeUnit(PydanticValueError): + msg_template = 'could not interpret byte unit: {unit}' + + +class MissingDiscriminator(PydanticValueError): + code = 'discriminated_union.missing_discriminator' + msg_template = 'Discriminator {discriminator_key!r} is missing in value' + + +class InvalidDiscriminator(PydanticValueError): + code = 'discriminated_union.invalid_discriminator' + msg_template = ( + 'No match for discriminator {discriminator_key!r} and value {discriminator_value!r} ' + '(allowed values: {allowed_values})' + ) + + def __init__(self, *, discriminator_key: str, discriminator_value: Any, allowed_values: Sequence[Any]) -> None: + super().__init__( + discriminator_key=discriminator_key, + discriminator_value=discriminator_value, + allowed_values=', '.join(map(repr, allowed_values)), + ) diff --git a/libs/win/pydantic/fields.cp37-win_amd64.pyd b/libs/win/pydantic/fields.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..2d31a5e9dafb28d9d17dd42897315140ff70619c GIT binary patch literal 391168 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%0AC7u#7y!_ICsVg* zzwe7+>jU+g6qa1@^YYB*DdVUI6Pg(H=yH5ARu5e6Mv9#&VeZ=rCyaWa9LRoAua{gz z^X+a~FBXi-?ypcQoy*ng18In8OzX{9sYLPzMRFIGvswc)YZHTRG&6O*LyWGsF^m1k zV$X9GY^?5gp52h{UkuLCSZ85(wqVFU;;Q-wIN(FFmr*DfV%-Jz9K5jsA1wxZW9}}6bt3Bz zv$*krhPc&{1qHWopxuVP3=gSDYGGnU&M~EvR4ldx))Z8>k5ZYj@?~LBBa3H9;*!pX zBspd_MACauQq5nfwe)IH{<*&2KzaixdgnyLBb|K9X+iGvh`$2p^&cv@;?HVX% z>MkREU!>JqzKB*+D2jlB>N!|CkN8QPDAgw@sxL|{^%oW9jFtZps;(tfJ7h3vwc6Sl zBDDqgrsVo2tNd~)KZfNWi7S7ds()^3shwq&e@n`bXZfq7y!}EMD#{$dEQCbjlh0
7(mQXm~EL?`d@JMT)2iioZMrNm>3XTh7MkE_ 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

u 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 MK;qATuK@@CKcFoCIRF3v literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/fields.py b/libs/win/pydantic/fields.py new file mode 100644 index 00000000..cecd3d20 --- /dev/null +++ b/libs/win/pydantic/fields.py @@ -0,0 +1,1247 @@ +import copy +import re +from collections import Counter as CollectionCounter, defaultdict, deque +from collections.abc import Callable, Hashable as CollectionsHashable, Iterable as CollectionsIterable +from typing import ( + TYPE_CHECKING, + Any, + Counter, + DefaultDict, + Deque, + Dict, + ForwardRef, + FrozenSet, + Generator, + Iterable, + Iterator, + List, + Mapping, + Optional, + Pattern, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from typing_extensions import Annotated, Final + +from . import errors as errors_ +from .class_validators import Validator, make_generic_validator, prep_validators +from .error_wrappers import ErrorWrapper +from .errors import ConfigError, InvalidDiscriminator, MissingDiscriminator, NoneIsNotAllowedError +from .types import Json, JsonWrapper +from .typing import ( + NoArgAnyCallable, + convert_generics, + display_as_type, + get_args, + get_origin, + is_finalvar, + is_literal_type, + is_new_type, + is_none_type, + is_typeddict, + is_typeddict_special, + is_union, + new_type_supertype, +) +from .utils import ( + PyObjectStr, + Representation, + ValueItems, + get_discriminator_alias_and_values, + get_unique_discriminator_alias, + lenient_isinstance, + lenient_issubclass, + sequence_like, + smart_deepcopy, +) +from .validators import constant_validator, dict_validator, find_validators, validate_json + +Required: Any = Ellipsis + +T = TypeVar('T') + + +class UndefinedType: + def __repr__(self) -> str: + return 'PydanticUndefined' + + def __copy__(self: T) -> T: + return self + + def __reduce__(self) -> str: + return 'Undefined' + + def __deepcopy__(self: T, _: Any) -> T: + return self + + +Undefined = UndefinedType() + +if TYPE_CHECKING: + from .class_validators import ValidatorsList + from .config import BaseConfig + from .error_wrappers import ErrorList + from .types import ModelOrDc + from .typing import AbstractSetIntStr, MappingIntStrAny, ReprArgs + + ValidateReturn = Tuple[Optional[Any], Optional[ErrorList]] + LocStr = Union[Tuple[Union[int, str], ...], str] + BoolUndefined = Union[bool, UndefinedType] + + +class FieldInfo(Representation): + """ + Captures extra information about a field. + """ + + __slots__ = ( + 'default', + 'default_factory', + 'alias', + 'alias_priority', + 'title', + 'description', + 'exclude', + 'include', + 'const', + 'gt', + 'ge', + 'lt', + 'le', + 'multiple_of', + 'allow_inf_nan', + 'max_digits', + 'decimal_places', + 'min_items', + 'max_items', + 'unique_items', + 'min_length', + 'max_length', + 'allow_mutation', + 'repr', + 'regex', + 'discriminator', + 'extra', + ) + + # field constraints with the default value, it's also used in update_from_config below + __field_constraints__ = { + 'min_length': None, + 'max_length': None, + 'regex': None, + 'gt': None, + 'lt': None, + 'ge': None, + 'le': None, + 'multiple_of': None, + 'allow_inf_nan': None, + 'max_digits': None, + 'decimal_places': None, + 'min_items': None, + 'max_items': None, + 'unique_items': None, + 'allow_mutation': True, + } + + def __init__(self, default: Any = Undefined, **kwargs: Any) -> None: + self.default = default + self.default_factory = kwargs.pop('default_factory', None) + self.alias = kwargs.pop('alias', None) + self.alias_priority = kwargs.pop('alias_priority', 2 if self.alias is not None else None) + self.title = kwargs.pop('title', None) + self.description = kwargs.pop('description', None) + self.exclude = kwargs.pop('exclude', None) + self.include = kwargs.pop('include', None) + self.const = kwargs.pop('const', None) + self.gt = kwargs.pop('gt', None) + self.ge = kwargs.pop('ge', None) + self.lt = kwargs.pop('lt', None) + self.le = kwargs.pop('le', None) + self.multiple_of = kwargs.pop('multiple_of', None) + self.allow_inf_nan = kwargs.pop('allow_inf_nan', None) + self.max_digits = kwargs.pop('max_digits', None) + self.decimal_places = kwargs.pop('decimal_places', None) + self.min_items = kwargs.pop('min_items', None) + self.max_items = kwargs.pop('max_items', None) + self.unique_items = kwargs.pop('unique_items', None) + self.min_length = kwargs.pop('min_length', None) + self.max_length = kwargs.pop('max_length', None) + self.allow_mutation = kwargs.pop('allow_mutation', True) + self.regex = kwargs.pop('regex', None) + self.discriminator = kwargs.pop('discriminator', None) + self.repr = kwargs.pop('repr', True) + self.extra = kwargs + + def __repr_args__(self) -> 'ReprArgs': + + field_defaults_to_hide: Dict[str, Any] = { + 'repr': True, + **self.__field_constraints__, + } + + attrs = ((s, getattr(self, s)) for s in self.__slots__) + return [(a, v) for a, v in attrs if v != field_defaults_to_hide.get(a, None)] + + def get_constraints(self) -> Set[str]: + """ + Gets the constraints set on the field by comparing the constraint value with its default value + + :return: the constraints set on field_info + """ + return {attr for attr, default in self.__field_constraints__.items() if getattr(self, attr) != default} + + def update_from_config(self, from_config: Dict[str, Any]) -> None: + """ + Update this FieldInfo based on a dict from get_field_info, only fields which have not been set are dated. + """ + for attr_name, value in from_config.items(): + try: + current_value = getattr(self, attr_name) + except AttributeError: + # attr_name is not an attribute of FieldInfo, it should therefore be added to extra + # (except if extra already has this value!) + self.extra.setdefault(attr_name, value) + else: + if current_value is self.__field_constraints__.get(attr_name, None): + setattr(self, attr_name, value) + elif attr_name == 'exclude': + self.exclude = ValueItems.merge(value, current_value) + elif attr_name == 'include': + self.include = ValueItems.merge(value, current_value, intersect=True) + + def _validate(self) -> None: + if self.default is not Undefined and self.default_factory is not None: + raise ValueError('cannot specify both default and default_factory') + + +def Field( + default: Any = Undefined, + *, + default_factory: Optional[NoArgAnyCallable] = None, + alias: str = None, + title: str = None, + description: str = None, + exclude: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None, + include: Union['AbstractSetIntStr', 'MappingIntStrAny', Any] = None, + const: bool = None, + gt: float = None, + ge: float = None, + lt: float = None, + le: float = None, + multiple_of: float = None, + allow_inf_nan: bool = None, + max_digits: int = None, + decimal_places: int = None, + min_items: int = None, + max_items: int = None, + unique_items: bool = None, + min_length: int = None, + max_length: int = None, + allow_mutation: bool = True, + regex: str = None, + discriminator: str = None, + repr: bool = True, + **extra: Any, +) -> Any: + """ + Used to provide extra information about a field, either for the model schema or complex validation. Some arguments + apply only to number fields (``int``, ``float``, ``Decimal``) and some apply only to ``str``. + + :param default: since this is replacing the field’s default, its first argument is used + to set the default, use ellipsis (``...``) to indicate the field is required + :param default_factory: callable that will be called when a default value is needed for this field + If both `default` and `default_factory` are set, an error is raised. + :param alias: the public name of the field + :param title: can be any string, used in the schema + :param description: can be any string, used in the schema + :param exclude: exclude this field while dumping. + Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method. + :param include: include this field while dumping. + Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method. + :param const: this field is required and *must* take it's default value + :param gt: only applies to numbers, requires the field to be "greater than". The schema + will have an ``exclusiveMinimum`` validation keyword + :param ge: only applies to numbers, requires the field to be "greater than or equal to". The + schema will have a ``minimum`` validation keyword + :param lt: only applies to numbers, requires the field to be "less than". The schema + will have an ``exclusiveMaximum`` validation keyword + :param le: only applies to numbers, requires the field to be "less than or equal to". The + schema will have a ``maximum`` validation keyword + :param multiple_of: only applies to numbers, requires the field to be "a multiple of". The + schema will have a ``multipleOf`` validation keyword + :param allow_inf_nan: only applies to numbers, allows the field to be NaN or infinity (+inf or -inf), + which is a valid Python float. Default True, set to False for compatibility with JSON. + :param max_digits: only applies to Decimals, requires the field to have a maximum number + of digits within the decimal. It does not include a zero before the decimal point or trailing decimal zeroes. + :param decimal_places: only applies to Decimals, requires the field to have at most a number of decimal places + allowed. It does not include trailing decimal zeroes. + :param min_items: only applies to lists, requires the field to have a minimum number of + elements. The schema will have a ``minItems`` validation keyword + :param max_items: only applies to lists, requires the field to have a maximum number of + elements. The schema will have a ``maxItems`` validation keyword + :param unique_items: only applies to lists, requires the field not to have duplicated + elements. The schema will have a ``uniqueItems`` validation keyword + :param min_length: only applies to strings, requires the field to have a minimum length. The + schema will have a ``maximum`` validation keyword + :param max_length: only applies to strings, requires the field to have a maximum length. The + schema will have a ``maxLength`` validation keyword + :param allow_mutation: a boolean which defaults to True. When False, the field raises a TypeError if the field is + assigned on an instance. The BaseModel Config must set validate_assignment to True + :param regex: only applies to strings, requires the field match against a regular expression + pattern string. The schema will have a ``pattern`` validation keyword + :param discriminator: only useful with a (discriminated a.k.a. tagged) `Union` of sub models with a common field. + The `discriminator` is the name of this common field to shorten validation and improve generated schema + :param repr: show this field in the representation + :param **extra: any additional keyword arguments will be added as is to the schema + """ + field_info = FieldInfo( + default, + default_factory=default_factory, + alias=alias, + title=title, + description=description, + exclude=exclude, + include=include, + const=const, + gt=gt, + ge=ge, + lt=lt, + le=le, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + min_items=min_items, + max_items=max_items, + unique_items=unique_items, + min_length=min_length, + max_length=max_length, + allow_mutation=allow_mutation, + regex=regex, + discriminator=discriminator, + repr=repr, + **extra, + ) + field_info._validate() + return field_info + + +# used to be an enum but changed to int's for small performance improvement as less access overhead +SHAPE_SINGLETON = 1 +SHAPE_LIST = 2 +SHAPE_SET = 3 +SHAPE_MAPPING = 4 +SHAPE_TUPLE = 5 +SHAPE_TUPLE_ELLIPSIS = 6 +SHAPE_SEQUENCE = 7 +SHAPE_FROZENSET = 8 +SHAPE_ITERABLE = 9 +SHAPE_GENERIC = 10 +SHAPE_DEQUE = 11 +SHAPE_DICT = 12 +SHAPE_DEFAULTDICT = 13 +SHAPE_COUNTER = 14 +SHAPE_NAME_LOOKUP = { + SHAPE_LIST: 'List[{}]', + SHAPE_SET: 'Set[{}]', + SHAPE_TUPLE_ELLIPSIS: 'Tuple[{}, ...]', + SHAPE_SEQUENCE: 'Sequence[{}]', + SHAPE_FROZENSET: 'FrozenSet[{}]', + SHAPE_ITERABLE: 'Iterable[{}]', + SHAPE_DEQUE: 'Deque[{}]', + SHAPE_DICT: 'Dict[{}]', + SHAPE_DEFAULTDICT: 'DefaultDict[{}]', + SHAPE_COUNTER: 'Counter[{}]', +} + +MAPPING_LIKE_SHAPES: Set[int] = {SHAPE_DEFAULTDICT, SHAPE_DICT, SHAPE_MAPPING, SHAPE_COUNTER} + + +class ModelField(Representation): + __slots__ = ( + 'type_', + 'outer_type_', + 'annotation', + 'sub_fields', + 'sub_fields_mapping', + 'key_field', + 'validators', + 'pre_validators', + 'post_validators', + 'default', + 'default_factory', + 'required', + 'final', + 'model_config', + 'name', + 'alias', + 'has_alias', + 'field_info', + 'discriminator_key', + 'discriminator_alias', + 'validate_always', + 'allow_none', + 'shape', + 'class_validators', + 'parse_json', + ) + + def __init__( + self, + *, + name: str, + type_: Type[Any], + class_validators: Optional[Dict[str, Validator]], + model_config: Type['BaseConfig'], + default: Any = None, + default_factory: Optional[NoArgAnyCallable] = None, + required: 'BoolUndefined' = Undefined, + final: bool = False, + alias: str = None, + field_info: Optional[FieldInfo] = None, + ) -> None: + + self.name: str = name + self.has_alias: bool = alias is not None + self.alias: str = alias if alias is not None else name + self.annotation = type_ + self.type_: Any = convert_generics(type_) + self.outer_type_: Any = type_ + self.class_validators = class_validators or {} + self.default: Any = default + self.default_factory: Optional[NoArgAnyCallable] = default_factory + self.required: 'BoolUndefined' = required + self.final: bool = final + self.model_config = model_config + self.field_info: FieldInfo = field_info or FieldInfo(default) + self.discriminator_key: Optional[str] = self.field_info.discriminator + self.discriminator_alias: Optional[str] = self.discriminator_key + + self.allow_none: bool = False + self.validate_always: bool = False + self.sub_fields: Optional[List[ModelField]] = None + self.sub_fields_mapping: Optional[Dict[str, 'ModelField']] = None # used for discriminated union + self.key_field: Optional[ModelField] = None + self.validators: 'ValidatorsList' = [] + self.pre_validators: Optional['ValidatorsList'] = None + self.post_validators: Optional['ValidatorsList'] = None + self.parse_json: bool = False + self.shape: int = SHAPE_SINGLETON + self.model_config.prepare_field(self) + self.prepare() + + def get_default(self) -> Any: + return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + @staticmethod + def _get_field_info( + field_name: str, annotation: Any, value: Any, config: Type['BaseConfig'] + ) -> Tuple[FieldInfo, Any]: + """ + Get a FieldInfo from a root typing.Annotated annotation, value, or config default. + + The FieldInfo may be set in typing.Annotated or the value, but not both. If neither contain + a FieldInfo, a new one will be created using the config. + + :param field_name: name of the field for use in error messages + :param annotation: a type hint such as `str` or `Annotated[str, Field(..., min_length=5)]` + :param value: the field's assigned value + :param config: the model's config object + :return: the FieldInfo contained in the `annotation`, the value, or a new one from the config. + """ + field_info_from_config = config.get_field_info(field_name) + + field_info = None + if get_origin(annotation) is Annotated: + field_infos = [arg for arg in get_args(annotation)[1:] if isinstance(arg, FieldInfo)] + if len(field_infos) > 1: + raise ValueError(f'cannot specify multiple `Annotated` `Field`s for {field_name!r}') + field_info = next(iter(field_infos), None) + if field_info is not None: + field_info = copy.copy(field_info) + field_info.update_from_config(field_info_from_config) + if field_info.default not in (Undefined, Required): + raise ValueError(f'`Field` default cannot be set in `Annotated` for {field_name!r}') + if value is not Undefined and value is not Required: + # check also `Required` because of `validate_arguments` that sets `...` as default value + field_info.default = value + + if isinstance(value, FieldInfo): + if field_info is not None: + raise ValueError(f'cannot specify `Annotated` and value `Field`s together for {field_name!r}') + field_info = value + field_info.update_from_config(field_info_from_config) + elif field_info is None: + field_info = FieldInfo(value, **field_info_from_config) + value = None if field_info.default_factory is not None else field_info.default + field_info._validate() + return field_info, value + + @classmethod + def infer( + cls, + *, + name: str, + value: Any, + annotation: Any, + class_validators: Optional[Dict[str, Validator]], + config: Type['BaseConfig'], + ) -> 'ModelField': + from .schema import get_annotation_from_field_info + + field_info, value = cls._get_field_info(name, annotation, value, config) + required: 'BoolUndefined' = Undefined + if value is Required: + required = True + value = None + elif value is not Undefined: + required = False + annotation = get_annotation_from_field_info(annotation, field_info, name, config.validate_assignment) + + return cls( + name=name, + type_=annotation, + alias=field_info.alias, + class_validators=class_validators, + default=value, + default_factory=field_info.default_factory, + required=required, + model_config=config, + field_info=field_info, + ) + + def set_config(self, config: Type['BaseConfig']) -> None: + self.model_config = config + info_from_config = config.get_field_info(self.name) + config.prepare_field(self) + new_alias = info_from_config.get('alias') + new_alias_priority = info_from_config.get('alias_priority') or 0 + if new_alias and new_alias_priority >= (self.field_info.alias_priority or 0): + self.field_info.alias = new_alias + self.field_info.alias_priority = new_alias_priority + self.alias = new_alias + new_exclude = info_from_config.get('exclude') + if new_exclude is not None: + self.field_info.exclude = ValueItems.merge(self.field_info.exclude, new_exclude) + new_include = info_from_config.get('include') + if new_include is not None: + self.field_info.include = ValueItems.merge(self.field_info.include, new_include, intersect=True) + + @property + def alt_alias(self) -> bool: + return self.name != self.alias + + def prepare(self) -> None: + """ + Prepare the field but inspecting self.default, self.type_ etc. + + Note: this method is **not** idempotent (because _type_analysis is not idempotent), + e.g. calling it it multiple times may modify the field and configure it incorrectly. + """ + self._set_default_and_type() + if self.type_.__class__ is ForwardRef or self.type_.__class__ is DeferredType: + # self.type_ is currently a ForwardRef and there's nothing we can do now, + # user will need to call model.update_forward_refs() + return + + self._type_analysis() + if self.required is Undefined: + self.required = True + if self.default is Undefined and self.default_factory is None: + self.default = None + self.populate_validators() + + def _set_default_and_type(self) -> None: + """ + Set the default value, infer the type if needed and check if `None` value is valid. + """ + if self.default_factory is not None: + if self.type_ is Undefined: + raise errors_.ConfigError( + f'you need to set the type of field {self.name!r} when using `default_factory`' + ) + return + + default_value = self.get_default() + + if default_value is not None and self.type_ is Undefined: + self.type_ = default_value.__class__ + self.outer_type_ = self.type_ + self.annotation = self.type_ + + if self.type_ is Undefined: + raise errors_.ConfigError(f'unable to infer type for attribute "{self.name}"') + + if self.required is False and default_value is None: + self.allow_none = True + + def _type_analysis(self) -> None: # noqa: C901 (ignore complexity) + # typing interface is horrible, we have to do some ugly checks + if lenient_issubclass(self.type_, JsonWrapper): + self.type_ = self.type_.inner_type + self.parse_json = True + elif lenient_issubclass(self.type_, Json): + self.type_ = Any + self.parse_json = True + elif isinstance(self.type_, TypeVar): + if self.type_.__bound__: + self.type_ = self.type_.__bound__ + elif self.type_.__constraints__: + self.type_ = Union[self.type_.__constraints__] + else: + self.type_ = Any + elif is_new_type(self.type_): + self.type_ = new_type_supertype(self.type_) + + if self.type_ is Any or self.type_ is object: + if self.required is Undefined: + self.required = False + self.allow_none = True + return + elif self.type_ is Pattern or self.type_ is re.Pattern: + # python 3.7 only, Pattern is a typing object but without sub fields + return + elif is_literal_type(self.type_): + return + elif is_typeddict(self.type_): + return + + if is_finalvar(self.type_): + self.final = True + + if self.type_ is Final: + self.type_ = Any + else: + self.type_ = get_args(self.type_)[0] + + self._type_analysis() + return + + origin = get_origin(self.type_) + + if origin is Annotated or is_typeddict_special(origin): + self.type_ = get_args(self.type_)[0] + self._type_analysis() + return + + if self.discriminator_key is not None and not is_union(origin): + raise TypeError('`discriminator` can only be used with `Union` type with more than one variant') + + # add extra check for `collections.abc.Hashable` for python 3.10+ where origin is not `None` + if origin is None or origin is CollectionsHashable: + # field is not "typing" object eg. Union, Dict, List etc. + # allow None for virtual superclasses of NoneType, e.g. Hashable + if isinstance(self.type_, type) and isinstance(None, self.type_): + self.allow_none = True + return + elif origin is Callable: + return + elif is_union(origin): + types_ = [] + for type_ in get_args(self.type_): + if is_none_type(type_) or type_ is Any or type_ is object: + if self.required is Undefined: + self.required = False + self.allow_none = True + if is_none_type(type_): + continue + types_.append(type_) + + if len(types_) == 1: + # Optional[] + self.type_ = types_[0] + # this is the one case where the "outer type" isn't just the original type + self.outer_type_ = self.type_ + # re-run to correctly interpret the new self.type_ + self._type_analysis() + else: + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{display_as_type(t)}') for t in types_] + + if self.discriminator_key is not None: + self.prepare_discriminated_union_sub_fields() + return + elif issubclass(origin, Tuple): # type: ignore + # origin == Tuple without item type + args = get_args(self.type_) + if not args: # plain tuple + self.type_ = Any + self.shape = SHAPE_TUPLE_ELLIPSIS + elif len(args) == 2 and args[1] is Ellipsis: # e.g. Tuple[int, ...] + self.type_ = args[0] + self.shape = SHAPE_TUPLE_ELLIPSIS + self.sub_fields = [self._create_sub_type(args[0], f'{self.name}_0')] + elif args == ((),): # Tuple[()] means empty tuple + self.shape = SHAPE_TUPLE + self.type_ = Any + self.sub_fields = [] + else: + self.shape = SHAPE_TUPLE + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(args)] + return + elif issubclass(origin, List): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'list_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_LIST + elif issubclass(origin, Set): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'set_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_SET + elif issubclass(origin, FrozenSet): + # Create self validators + get_validators = getattr(self.type_, '__get_validators__', None) + if get_validators: + self.class_validators.update( + {f'frozenset_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())} + ) + + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_FROZENSET + elif issubclass(origin, Deque): + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_DEQUE + elif issubclass(origin, Sequence): + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_SEQUENCE + # priority to most common mapping: dict + elif origin is dict or origin is Dict: + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_DICT + elif issubclass(origin, DefaultDict): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_DEFAULTDICT + elif issubclass(origin, Counter): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = int + self.shape = SHAPE_COUNTER + elif issubclass(origin, Mapping): + self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True) + self.type_ = get_args(self.type_)[1] + self.shape = SHAPE_MAPPING + # Equality check as almost everything inherits form Iterable, including str + # check for Iterable and CollectionsIterable, as it could receive one even when declared with the other + elif origin in {Iterable, CollectionsIterable}: + self.type_ = get_args(self.type_)[0] + self.shape = SHAPE_ITERABLE + self.sub_fields = [self._create_sub_type(self.type_, f'{self.name}_type')] + elif issubclass(origin, Type): # type: ignore + return + elif hasattr(origin, '__get_validators__') or self.model_config.arbitrary_types_allowed: + # Is a Pydantic-compatible generic that handles itself + # or we have arbitrary_types_allowed = True + self.shape = SHAPE_GENERIC + self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(get_args(self.type_))] + self.type_ = origin + return + else: + raise TypeError(f'Fields of type "{origin}" are not supported.') + + # type_ has been refined eg. as the type of a List and sub_fields needs to be populated + self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)] + + def prepare_discriminated_union_sub_fields(self) -> None: + """ + Prepare the mapping -> and update `sub_fields` + Note that this process can be aborted if a `ForwardRef` is encountered + """ + assert self.discriminator_key is not None + + if self.type_.__class__ is DeferredType: + return + + assert self.sub_fields is not None + sub_fields_mapping: Dict[str, 'ModelField'] = {} + all_aliases: Set[str] = set() + + for sub_field in self.sub_fields: + t = sub_field.type_ + if t.__class__ is ForwardRef: + # Stopping everything...will need to call `update_forward_refs` + return + + alias, discriminator_values = get_discriminator_alias_and_values(t, self.discriminator_key) + all_aliases.add(alias) + for discriminator_value in discriminator_values: + sub_fields_mapping[discriminator_value] = sub_field + + self.sub_fields_mapping = sub_fields_mapping + self.discriminator_alias = get_unique_discriminator_alias(all_aliases, self.discriminator_key) + + def _create_sub_type(self, type_: Type[Any], name: str, *, for_keys: bool = False) -> 'ModelField': + if for_keys: + class_validators = None + else: + # validators for sub items should not have `each_item` as we want to check only the first sublevel + class_validators = { + k: Validator( + func=v.func, + pre=v.pre, + each_item=False, + always=v.always, + check_fields=v.check_fields, + skip_on_failure=v.skip_on_failure, + ) + for k, v in self.class_validators.items() + if v.each_item + } + + field_info, _ = self._get_field_info(name, type_, None, self.model_config) + + return self.__class__( + type_=type_, + name=name, + class_validators=class_validators, + model_config=self.model_config, + field_info=field_info, + ) + + def populate_validators(self) -> None: + """ + Prepare self.pre_validators, self.validators, and self.post_validators based on self.type_'s __get_validators__ + and class validators. This method should be idempotent, e.g. it should be safe to call multiple times + without mis-configuring the field. + """ + self.validate_always = getattr(self.type_, 'validate_always', False) or any( + v.always for v in self.class_validators.values() + ) + + class_validators_ = self.class_validators.values() + if not self.sub_fields or self.shape == SHAPE_GENERIC: + get_validators = getattr(self.type_, '__get_validators__', None) + v_funcs = ( + *[v.func for v in class_validators_ if v.each_item and v.pre], + *(get_validators() if get_validators else list(find_validators(self.type_, self.model_config))), + *[v.func for v in class_validators_ if v.each_item and not v.pre], + ) + self.validators = prep_validators(v_funcs) + + self.pre_validators = [] + self.post_validators = [] + + if self.field_info and self.field_info.const: + self.post_validators.append(make_generic_validator(constant_validator)) + + if class_validators_: + self.pre_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and v.pre) + self.post_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and not v.pre) + + if self.parse_json: + self.pre_validators.append(make_generic_validator(validate_json)) + + self.pre_validators = self.pre_validators or None + self.post_validators = self.post_validators or None + + def validate( + self, v: Any, values: Dict[str, Any], *, loc: 'LocStr', cls: Optional['ModelOrDc'] = None + ) -> 'ValidateReturn': + + assert self.type_.__class__ is not DeferredType + + if self.type_.__class__ is ForwardRef: + assert cls is not None + raise ConfigError( + f'field "{self.name}" not yet prepared so type is still a ForwardRef, ' + f'you might need to call {cls.__name__}.update_forward_refs().' + ) + + errors: Optional['ErrorList'] + if self.pre_validators: + v, errors = self._apply_validators(v, values, loc, cls, self.pre_validators) + if errors: + return v, errors + + if v is None: + if is_none_type(self.type_): + # keep validating + pass + elif self.allow_none: + if self.post_validators: + return self._apply_validators(v, values, loc, cls, self.post_validators) + else: + return None, None + else: + return v, ErrorWrapper(NoneIsNotAllowedError(), loc) + + if self.shape == SHAPE_SINGLETON: + v, errors = self._validate_singleton(v, values, loc, cls) + elif self.shape in MAPPING_LIKE_SHAPES: + v, errors = self._validate_mapping_like(v, values, loc, cls) + elif self.shape == SHAPE_TUPLE: + v, errors = self._validate_tuple(v, values, loc, cls) + elif self.shape == SHAPE_ITERABLE: + v, errors = self._validate_iterable(v, values, loc, cls) + elif self.shape == SHAPE_GENERIC: + v, errors = self._apply_validators(v, values, loc, cls, self.validators) + else: + # sequence, list, set, generator, tuple with ellipsis, frozen set + v, errors = self._validate_sequence_like(v, values, loc, cls) + + if not errors and self.post_validators: + v, errors = self._apply_validators(v, values, loc, cls, self.post_validators) + return v, errors + + def _validate_sequence_like( # noqa: C901 (ignore complexity) + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + """ + Validate sequence-like containers: lists, tuples, sets and generators + Note that large if-else blocks are necessary to enable Cython + optimization, which is why we disable the complexity check above. + """ + if not sequence_like(v): + e: errors_.PydanticTypeError + if self.shape == SHAPE_LIST: + e = errors_.ListError() + elif self.shape in (SHAPE_TUPLE, SHAPE_TUPLE_ELLIPSIS): + e = errors_.TupleError() + elif self.shape == SHAPE_SET: + e = errors_.SetError() + elif self.shape == SHAPE_FROZENSET: + e = errors_.FrozenSetError() + else: + e = errors_.SequenceError() + return v, ErrorWrapper(e, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result = [] + errors: List[ErrorList] = [] + for i, v_ in enumerate(v): + v_loc = *loc, i + r, ee = self._validate_singleton(v_, values, v_loc, cls) + if ee: + errors.append(ee) + else: + result.append(r) + + if errors: + return v, errors + + converted: Union[List[Any], Set[Any], FrozenSet[Any], Tuple[Any, ...], Iterator[Any], Deque[Any]] = result + + if self.shape == SHAPE_SET: + converted = set(result) + elif self.shape == SHAPE_FROZENSET: + converted = frozenset(result) + elif self.shape == SHAPE_TUPLE_ELLIPSIS: + converted = tuple(result) + elif self.shape == SHAPE_DEQUE: + converted = deque(result) + elif self.shape == SHAPE_SEQUENCE: + if isinstance(v, tuple): + converted = tuple(result) + elif isinstance(v, set): + converted = set(result) + elif isinstance(v, Generator): + converted = iter(result) + elif isinstance(v, deque): + converted = deque(result) + return converted, None + + def _validate_iterable( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + """ + Validate Iterables. + + This intentionally doesn't validate values to allow infinite generators. + """ + + try: + iterable = iter(v) + except TypeError: + return v, ErrorWrapper(errors_.IterableError(), loc) + return iterable, None + + def _validate_tuple( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + e: Optional[Exception] = None + if not sequence_like(v): + e = errors_.TupleError() + else: + actual_length, expected_length = len(v), len(self.sub_fields) # type: ignore + if actual_length != expected_length: + e = errors_.TupleLengthError(actual_length=actual_length, expected_length=expected_length) + + if e: + return v, ErrorWrapper(e, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result = [] + errors: List[ErrorList] = [] + for i, (v_, field) in enumerate(zip(v, self.sub_fields)): # type: ignore + v_loc = *loc, i + r, ee = field.validate(v_, values, loc=v_loc, cls=cls) + if ee: + errors.append(ee) + else: + result.append(r) + + if errors: + return v, errors + else: + return tuple(result), None + + def _validate_mapping_like( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + try: + v_iter = dict_validator(v) + except TypeError as exc: + return v, ErrorWrapper(exc, loc) + + loc = loc if isinstance(loc, tuple) else (loc,) + result, errors = {}, [] + for k, v_ in v_iter.items(): + v_loc = *loc, '__key__' + key_result, key_errors = self.key_field.validate(k, values, loc=v_loc, cls=cls) # type: ignore + if key_errors: + errors.append(key_errors) + continue + + v_loc = *loc, k + value_result, value_errors = self._validate_singleton(v_, values, v_loc, cls) + if value_errors: + errors.append(value_errors) + continue + + result[key_result] = value_result + if errors: + return v, errors + elif self.shape == SHAPE_DICT: + return result, None + elif self.shape == SHAPE_DEFAULTDICT: + return defaultdict(self.type_, result), None + elif self.shape == SHAPE_COUNTER: + return CollectionCounter(result), None + else: + return self._get_mapping_value(v, result), None + + def _get_mapping_value(self, original: T, converted: Dict[Any, Any]) -> Union[T, Dict[Any, Any]]: + """ + When type is `Mapping[KT, KV]` (or another unsupported mapping), we try to avoid + coercing to `dict` unwillingly. + """ + original_cls = original.__class__ + + if original_cls == dict or original_cls == Dict: + return converted + elif original_cls in {defaultdict, DefaultDict}: + return defaultdict(self.type_, converted) + else: + try: + # Counter, OrderedDict, UserDict, ... + return original_cls(converted) # type: ignore + except TypeError: + raise RuntimeError(f'Could not convert dictionary to {original_cls.__name__!r}') from None + + def _validate_singleton( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + if self.sub_fields: + if self.discriminator_key is not None: + return self._validate_discriminated_union(v, values, loc, cls) + + errors = [] + + if self.model_config.smart_union and is_union(get_origin(self.type_)): + # 1st pass: check if the value is an exact instance of one of the Union types + # (e.g. to avoid coercing a bool into an int) + for field in self.sub_fields: + if v.__class__ is field.outer_type_: + return v, None + + # 2nd pass: check if the value is an instance of any subclass of the Union types + for field in self.sub_fields: + # This whole logic will be improved later on to support more complex `isinstance` checks + # It will probably be done once a strict mode is added and be something like: + # ``` + # value, error = field.validate(v, values, strict=True) + # if error is None: + # return value, None + # ``` + try: + if isinstance(v, field.outer_type_): + return v, None + except TypeError: + # compound type + if lenient_isinstance(v, get_origin(field.outer_type_)): + value, error = field.validate(v, values, loc=loc, cls=cls) + if not error: + return value, None + + # 1st pass by default or 3rd pass with `smart_union` enabled: + # check if the value can be coerced into one of the Union types + for field in self.sub_fields: + value, error = field.validate(v, values, loc=loc, cls=cls) + if error: + errors.append(error) + else: + return value, None + return v, errors + else: + return self._apply_validators(v, values, loc, cls, self.validators) + + def _validate_discriminated_union( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'] + ) -> 'ValidateReturn': + assert self.discriminator_key is not None + assert self.discriminator_alias is not None + + try: + discriminator_value = v[self.discriminator_alias] + except KeyError: + return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc) + except TypeError: + try: + # BaseModel or dataclass + discriminator_value = getattr(v, self.discriminator_key) + except (AttributeError, TypeError): + return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc) + + try: + sub_field = self.sub_fields_mapping[discriminator_value] # type: ignore[index] + except TypeError: + assert cls is not None + raise ConfigError( + f'field "{self.name}" not yet prepared so type is still a ForwardRef, ' + f'you might need to call {cls.__name__}.update_forward_refs().' + ) + except KeyError: + assert self.sub_fields_mapping is not None + return v, ErrorWrapper( + InvalidDiscriminator( + discriminator_key=self.discriminator_key, + discriminator_value=discriminator_value, + allowed_values=list(self.sub_fields_mapping), + ), + loc, + ) + else: + if not isinstance(loc, tuple): + loc = (loc,) + return sub_field.validate(v, values, loc=(*loc, display_as_type(sub_field.type_)), cls=cls) + + def _apply_validators( + self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'], validators: 'ValidatorsList' + ) -> 'ValidateReturn': + for validator in validators: + try: + v = validator(cls, v, values, self, self.model_config) + except (ValueError, TypeError, AssertionError) as exc: + return v, ErrorWrapper(exc, loc) + return v, None + + def is_complex(self) -> bool: + """ + Whether the field is "complex" eg. env variables should be parsed as JSON. + """ + from .main import BaseModel + + return ( + self.shape != SHAPE_SINGLETON + or hasattr(self.type_, '__pydantic_model__') + or lenient_issubclass(self.type_, (BaseModel, list, set, frozenset, dict)) + ) + + def _type_display(self) -> PyObjectStr: + t = display_as_type(self.type_) + + if self.shape in MAPPING_LIKE_SHAPES: + t = f'Mapping[{display_as_type(self.key_field.type_)}, {t}]' # type: ignore + elif self.shape == SHAPE_TUPLE: + t = 'Tuple[{}]'.format(', '.join(display_as_type(f.type_) for f in self.sub_fields)) # type: ignore + elif self.shape == SHAPE_GENERIC: + assert self.sub_fields + t = '{}[{}]'.format( + display_as_type(self.type_), ', '.join(display_as_type(f.type_) for f in self.sub_fields) + ) + elif self.shape != SHAPE_SINGLETON: + t = SHAPE_NAME_LOOKUP[self.shape].format(t) + + if self.allow_none and (self.shape != SHAPE_SINGLETON or not self.sub_fields): + t = f'Optional[{t}]' + return PyObjectStr(t) + + def __repr_args__(self) -> 'ReprArgs': + args = [('name', self.name), ('type', self._type_display()), ('required', self.required)] + + if not self.required: + if self.default_factory is not None: + args.append(('default_factory', f'')) + else: + args.append(('default', self.default)) + + if self.alt_alias: + args.append(('alias', self.alias)) + return args + + +class ModelPrivateAttr(Representation): + __slots__ = ('default', 'default_factory') + + def __init__(self, default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None) -> None: + self.default = default + self.default_factory = default_factory + + def get_default(self) -> Any: + return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and (self.default, self.default_factory) == ( + other.default, + other.default_factory, + ) + + +def PrivateAttr( + default: Any = Undefined, + *, + default_factory: Optional[NoArgAnyCallable] = None, +) -> Any: + """ + Indicates that attribute is only used internally and never mixed with regular fields. + + Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant. + + Private attrs are stored in model __slots__. + + :param default: the attribute’s default value + :param default_factory: callable that will be called when a default value is needed for this attribute + If both `default` and `default_factory` are set, an error is raised. + """ + if default is not Undefined and default_factory is not None: + raise ValueError('cannot specify both default and default_factory') + + return ModelPrivateAttr( + default, + default_factory=default_factory, + ) + + +class DeferredType: + """ + Used to postpone field preparation, while creating recursive generic models. + """ + + +def is_finalvar_with_default_val(type_: Type[Any], val: Any) -> bool: + return is_finalvar(type_) and val is not Undefined and not isinstance(val, FieldInfo) diff --git a/libs/win/pydantic/generics.py b/libs/win/pydantic/generics.py new file mode 100644 index 00000000..a3f52bfe --- /dev/null +++ b/libs/win/pydantic/generics.py @@ -0,0 +1,364 @@ +import sys +import typing +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Dict, + Generic, + Iterator, + List, + Mapping, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +from typing_extensions import Annotated + +from .class_validators import gather_all_validators +from .fields import DeferredType +from .main import BaseModel, create_model +from .types import JsonWrapper +from .typing import display_as_type, get_all_type_hints, get_args, get_origin, typing_base +from .utils import LimitedDict, all_identical, lenient_issubclass + +GenericModelT = TypeVar('GenericModelT', bound='GenericModel') +TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type + +Parametrization = Mapping[TypeVarType, Type[Any]] + +_generic_types_cache: LimitedDict[Tuple[Type[Any], Union[Any, Tuple[Any, ...]]], Type[BaseModel]] = LimitedDict() +# _assigned_parameters is a Mapping from parametrized version of generic models to assigned types of parametrizations +# as captured during construction of the class (not instances). +# E.g., for generic model `Model[A, B]`, when parametrized model `Model[int, str]` is created, +# `Model[int, str]`: {A: int, B: str}` will be stored in `_assigned_parameters`. +# (This information is only otherwise available after creation from the class name string). +_assigned_parameters: LimitedDict[Type[Any], Parametrization] = LimitedDict() + + +class GenericModel(BaseModel): + __slots__ = () + __concrete__: ClassVar[bool] = False + + if TYPE_CHECKING: + # Putting this in a TYPE_CHECKING block allows us to replace `if Generic not in cls.__bases__` with + # `not hasattr(cls, "__parameters__")`. This means we don't need to force non-concrete subclasses of + # `GenericModel` to also inherit from `Generic`, which would require changes to the use of `create_model` below. + __parameters__: ClassVar[Tuple[TypeVarType, ...]] + + # Setting the return type as Type[Any] instead of Type[BaseModel] prevents PyCharm warnings + def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[Type[Any], ...]]) -> Type[Any]: + """Instantiates a new class from a generic class `cls` and type variables `params`. + + :param params: Tuple of types the class . Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + :return: New model class inheriting from `cls` with instantiated + types described by `params`. If no parameters are given, `cls` is + returned as is. + + """ + + def _cache_key(_params: Any) -> Tuple[Type[GenericModelT], Any, Tuple[Any, ...]]: + return cls, _params, get_args(_params) + + cached = _generic_types_cache.get(_cache_key(params)) + if cached is not None: + return cached + if cls.__concrete__ and Generic not in cls.__bases__: + raise TypeError('Cannot parameterize a concrete instantiation of a generic model') + if not isinstance(params, tuple): + params = (params,) + if cls is GenericModel and any(isinstance(param, TypeVar) for param in params): + raise TypeError('Type parameters should be placed on typing.Generic, not GenericModel') + if not hasattr(cls, '__parameters__'): + raise TypeError(f'Type {cls.__name__} must inherit from typing.Generic before being parameterized') + + check_parameters_count(cls, params) + # Build map from generic typevars to passed params + typevars_map: Dict[TypeVarType, Type[Any]] = dict(zip(cls.__parameters__, params)) + if all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map: + return cls # if arguments are equal to parameters it's the same object + + # Create new model with original model as parent inserting fields with DeferredType. + model_name = cls.__concrete_name__(params) + validators = gather_all_validators(cls) + + type_hints = get_all_type_hints(cls).items() + instance_type_hints = {k: v for k, v in type_hints if get_origin(v) is not ClassVar} + + fields = {k: (DeferredType(), cls.__fields__[k].field_info) for k in instance_type_hints if k in cls.__fields__} + + model_module, called_globally = get_caller_frame_info() + created_model = cast( + Type[GenericModel], # casting ensures mypy is aware of the __concrete__ and __parameters__ attributes + create_model( + model_name, + __module__=model_module or cls.__module__, + __base__=(cls,) + tuple(cls.__parameterized_bases__(typevars_map)), + __config__=None, + __validators__=validators, + __cls_kwargs__=None, + **fields, + ), + ) + + _assigned_parameters[created_model] = typevars_map + + if called_globally: # create global reference and therefore allow pickling + object_by_reference = None + reference_name = model_name + reference_module_globals = sys.modules[created_model.__module__].__dict__ + while object_by_reference is not created_model: + object_by_reference = reference_module_globals.setdefault(reference_name, created_model) + reference_name += '_' + + created_model.Config = cls.Config + + # Find any typevars that are still present in the model. + # If none are left, the model is fully "concrete", otherwise the new + # class is a generic class as well taking the found typevars as + # parameters. + new_params = tuple( + {param: None for param in iter_contained_typevars(typevars_map.values())} + ) # use dict as ordered set + created_model.__concrete__ = not new_params + if new_params: + created_model.__parameters__ = new_params + + # Save created model in cache so we don't end up creating duplicate + # models that should be identical. + _generic_types_cache[_cache_key(params)] = created_model + if len(params) == 1: + _generic_types_cache[_cache_key(params[0])] = created_model + + # Recursively walk class type hints and replace generic typevars + # with concrete types that were passed. + _prepare_model_fields(created_model, fields, instance_type_hints, typevars_map) + + return created_model + + @classmethod + def __concrete_name__(cls: Type[Any], params: Tuple[Type[Any], ...]) -> str: + """Compute class name for child classes. + + :param params: Tuple of types the class . Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + :return: String representing a the new class where `params` are + passed to `cls` as type variables. + + This method can be overridden to achieve a custom naming scheme for GenericModels. + """ + param_names = [display_as_type(param) for param in params] + params_component = ', '.join(param_names) + return f'{cls.__name__}[{params_component}]' + + @classmethod + def __parameterized_bases__(cls, typevars_map: Parametrization) -> Iterator[Type[Any]]: + """ + Returns unbound bases of cls parameterised to given type variables + + :param typevars_map: Dictionary of type applications for binding subclasses. + Given a generic class `Model` with 2 type variables [S, T] + and a concrete model `Model[str, int]`, + the value `{S: str, T: int}` would be passed to `typevars_map`. + :return: an iterator of generic sub classes, parameterised by `typevars_map` + and other assigned parameters of `cls` + + e.g.: + ``` + class A(GenericModel, Generic[T]): + ... + + class B(A[V], Generic[V]): + ... + + assert A[int] in B.__parameterized_bases__({V: int}) + ``` + """ + + def build_base_model( + base_model: Type[GenericModel], mapped_types: Parametrization + ) -> Iterator[Type[GenericModel]]: + base_parameters = tuple(mapped_types[param] for param in base_model.__parameters__) + parameterized_base = base_model.__class_getitem__(base_parameters) + if parameterized_base is base_model or parameterized_base is cls: + # Avoid duplication in MRO + return + yield parameterized_base + + for base_model in cls.__bases__: + if not issubclass(base_model, GenericModel): + # not a class that can be meaningfully parameterized + continue + elif not getattr(base_model, '__parameters__', None): + # base_model is "GenericModel" (and has no __parameters__) + # or + # base_model is already concrete, and will be included transitively via cls. + continue + elif cls in _assigned_parameters: + if base_model in _assigned_parameters: + # cls is partially parameterised but not from base_model + # e.g. cls = B[S], base_model = A[S] + # B[S][int] should subclass A[int], (and will be transitively via B[int]) + # but it's not viable to consistently subclass types with arbitrary construction + # So don't attempt to include A[S][int] + continue + else: # base_model not in _assigned_parameters: + # cls is partially parameterized, base_model is original generic + # e.g. cls = B[str, T], base_model = B[S, T] + # Need to determine the mapping for the base_model parameters + mapped_types: Parametrization = { + key: typevars_map.get(value, value) for key, value in _assigned_parameters[cls].items() + } + yield from build_base_model(base_model, mapped_types) + else: + # cls is base generic, so base_class has a distinct base + # can construct the Parameterised base model using typevars_map directly + yield from build_base_model(base_model, typevars_map) + + +def replace_types(type_: Any, type_map: Mapping[Any, Any]) -> Any: + """Return type with all occurrences of `type_map` keys recursively replaced with their values. + + :param type_: Any type, class or generic alias + :param type_map: Mapping from `TypeVar` instance to concrete types. + :return: New type representing the basic structure of `type_` with all + `typevar_map` keys recursively replaced. + + >>> replace_types(Tuple[str, Union[List[str], float]], {str: int}) + Tuple[int, Union[List[int], float]] + + """ + if not type_map: + return type_ + + type_args = get_args(type_) + origin_type = get_origin(type_) + + if origin_type is Annotated: + annotated_type, *annotations = type_args + return Annotated[replace_types(annotated_type, type_map), tuple(annotations)] + + # Having type args is a good indicator that this is a typing module + # class instantiation or a generic alias of some sort. + if type_args: + resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args) + if all_identical(type_args, resolved_type_args): + # If all arguments are the same, there is no need to modify the + # type or create a new object at all + return type_ + if ( + origin_type is not None + and isinstance(type_, typing_base) + and not isinstance(origin_type, typing_base) + and getattr(type_, '_name', None) is not None + ): + # In python < 3.9 generic aliases don't exist so any of these like `list`, + # `type` or `collections.abc.Callable` need to be translated. + # See: https://www.python.org/dev/peps/pep-0585 + origin_type = getattr(typing, type_._name) + assert origin_type is not None + return origin_type[resolved_type_args] + + # We handle pydantic generic models separately as they don't have the same + # semantics as "typing" classes or generic aliases + if not origin_type and lenient_issubclass(type_, GenericModel) and not type_.__concrete__: + type_args = type_.__parameters__ + resolved_type_args = tuple(replace_types(t, type_map) for t in type_args) + if all_identical(type_args, resolved_type_args): + return type_ + return type_[resolved_type_args] + + # Handle special case for typehints that can have lists as arguments. + # `typing.Callable[[int, str], int]` is an example for this. + if isinstance(type_, (List, list)): + resolved_list = list(replace_types(element, type_map) for element in type_) + if all_identical(type_, resolved_list): + return type_ + return resolved_list + + # For JsonWrapperValue, need to handle its inner type to allow correct parsing + # of generic Json arguments like Json[T] + if not origin_type and lenient_issubclass(type_, JsonWrapper): + type_.inner_type = replace_types(type_.inner_type, type_map) + return type_ + + # If all else fails, we try to resolve the type directly and otherwise just + # return the input with no modifications. + return type_map.get(type_, type_) + + +def check_parameters_count(cls: Type[GenericModel], parameters: Tuple[Any, ...]) -> None: + actual = len(parameters) + expected = len(cls.__parameters__) + if actual != expected: + description = 'many' if actual > expected else 'few' + raise TypeError(f'Too {description} parameters for {cls.__name__}; actual {actual}, expected {expected}') + + +DictValues: Type[Any] = {}.values().__class__ + + +def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]: + """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found.""" + if isinstance(v, TypeVar): + yield v + elif hasattr(v, '__parameters__') and not get_origin(v) and lenient_issubclass(v, GenericModel): + yield from v.__parameters__ + elif isinstance(v, (DictValues, list)): + for var in v: + yield from iter_contained_typevars(var) + else: + args = get_args(v) + for arg in args: + yield from iter_contained_typevars(arg) + + +def get_caller_frame_info() -> Tuple[Optional[str], bool]: + """ + Used inside a function to check whether it was called globally + + Will only work against non-compiled code, therefore used only in pydantic.generics + + :returns Tuple[module_name, called_globally] + """ + try: + previous_caller_frame = sys._getframe(2) + except ValueError as e: + raise RuntimeError('This function must be used inside another function') from e + except AttributeError: # sys module does not have _getframe function, so there's nothing we can do about it + return None, False + frame_globals = previous_caller_frame.f_globals + return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals + + +def _prepare_model_fields( + created_model: Type[GenericModel], + fields: Mapping[str, Any], + instance_type_hints: Mapping[str, type], + typevars_map: Mapping[Any, type], +) -> None: + """ + Replace DeferredType fields with concrete type hints and prepare them. + """ + + for key, field in created_model.__fields__.items(): + if key not in fields: + assert field.type_.__class__ is not DeferredType + # https://github.com/nedbat/coveragepy/issues/198 + continue # pragma: no cover + + assert field.type_.__class__ is DeferredType, field.type_.__class__ + + field_type_hint = instance_type_hints[key] + concrete_type = replace_types(field_type_hint, typevars_map) + field.type_ = concrete_type + field.outer_type_ = concrete_type + field.prepare() + created_model.__annotations__[key] = concrete_type diff --git a/libs/win/pydantic/json.cp37-win_amd64.pyd b/libs/win/pydantic/json.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..b01a561ad61e156ddda1becf691252ca61a542cc GIT binary patch literal 69632 zcmd?Sd3+RA_BY&F8p6_a5DFrKv=TIkAQI7FK+}*&wRA8FXp}9A2r6KNZjH)nCrVqi zI&R~PIxgclI-?G5h$wc#mLNNh8pQ>1nQGewHx?bI-tYHRRc{IN{GR9Yy#Kr}Gp6d^ zy7%n&+;h*ZzEyeI-HsfG!;y>s48!4AgDd})$>0C}CqK*K=+=L2H^)?29RIizKn*)v$JzcW9o#0r7 znmK0eftPU2m-}T9gL$9raCDHWAWc5XDX4$M+w%_5X#?w|@uVm-4 zEXQG9hh;e$Z6qvLWjR&=@2dA4j=n4(4S^T`{che zKp0wUHWVJgt0NE3vvE!NS4MaX7A~NIL?)1p12S-QZm->}x!0i~WkgvC4_(`|8-u6+ zzx-D=Q4M|VQMEC7er>&~DL-|~a`?0szqaCT$fL4hOTg(hcE>vy$`!D9H5@1$rfRKf zcu`@A+Rzy6u4$@A!@o4fokq?vd+WRK3<^$`0k=I>Nv3JdIQ(m|J{{M!&U|mH?>w4F9G->71W?)dq z`!&}vkXjxoJ7euwk{fXqfGqvT2ZWr^#-a_W`bdZW=25NpN%$`t{;qWbzijP76Mic+ zrQh2={8$%ABL!im{{MMYj)oIsc&;^G0h;s{6R;l?4 z)QGZ~+S582%16)gX2g8rd6RfxogA0;Q1)v(pwL&*+`FbH?d%=B&;3+vJV(`Y;KQmg4?r@0ZJFuA8tKD!(TDvBl&9&$j{zz= z=@zLw$@^wDW}Db9%nne<7_wq1WSpv93x#|WI49x7D$u5_8+(Utta>3CpXadsSOrwF zJ)x3277)DnF-6sejD6kVh%5o_eBy4VF=Y5MOQ&FLHYToixN4#4z$s(+wU8T?a(iTe$kVJ>JD;{i1;J7>p_*Avjx$AEZN6-mtk9Xw+ zpd1!T#fUG7o(wqNHw#JRDQLSgqisM8P}&ZT4wAN`OhP|+4OXjdhxygOkju4ZFD8XY z?nxM*A9h!kVtm@vLX{RcBhBBmu|I928m`J$!)o5dOVs6GGagaHbM#_E4cG1~KIjkE zv{n>6N znXXueEQh}FdV+veHU=>i0Qf-yzL9|2n4=Qy$N{!PzfMe94A1QJ5Ic<#YVy?Z!xnpa zYQq-VVi~Y;a)vG5Z$TK63E?q9aF`JM+OGt%41!R@nLvv7W?Il|jOq*}`95xExy=_qyhIS!5JcNymCe40Pp(Ll zAUdsPrxAh|4gRBsUl#SM;SQ8?%GfME*jC2x`7{hTLwWXTha+^@fKqO$^hP%l^^yQ|u-@oqGXr{)p8#C|*}eGY$; z#_~XJyT-DV_K>jqiQec4c3pr!R*(QY>(YMLa_MUO4|F|?UHPZ2ZST-G3rkRlcpJ1n zo2@%Z>x@~1A$_TH9RwGcPJjO-ZYHbE%?AEq*s0++3K4>1+@FmnNe>2?Uc!6@Hb>9n z*H-O=H2fHZ9yG7SD&iDxWC@y$WwSfk%*ZJ=Dlix=kak;D`>_oVkqHHGK;e~zMF6I1 zyVbD2u-4tbOaPxR({AaKe0St5Ot+x=KcFqxA8Q>oe4o{&?_uCmjb7&J3Yd72l@B&*oWT+rR^*G{Nx z_&nhBm+lW@sCr?92#-{)CH{q9yB=b?DW7~lCbD{?5Njr|^m!oHXXF+_Xn`D`(V-IK z2v^{9O?J}x0m?|{S2Cb{=J<)3z?-v%RVPz7i zQp3*0ya=a;BYvEr{6?Ta5MTsdcO^Rl;WvRR&0(z8Qmj2lAbVkOzs(SC7gwUZ)4!lo z36~dE`Pr}0-S8&tz^{!6BC?ItP4-5Yi!En(20x{nK+rEdj>fK+$=t;lb1opos0(}0S9 zxq*)QQh39-Q?HJg6UKD3sgwZx0XSIi8o{rk}3v2iXup4Nuey!d#E|@WmOV#pJtq%Xqf!%rh zVT{zUn%~x_+xOj?Ir#mWIjT7}%mF#65E}f(T|e*N@DdcLyWtAnCY*Fy)7W`-vs6Dy zyFbPkI<+t>*u|%n7eXD|=$J7I@_jOcSq@byF|}k)OFp=DpY{=k#KA|wX*0})n(>XS z$O~TJ(-vdw8G-YB+B9=o%W0!|3adaI493b*d!i$#MY?!q$4 z8+{xwR7A;jV%DN*zok{5_D@^U94)tFwD>hdv9TclWhhG@M^B8azOd)L6phxRYRBlS z?x4X13Jv8dRKA9$XuJ0@_T9(9>hVT@CEXvwAO&1>BAR%kGg!Gztkn6>e`8=R6*6BI zfiOB^)azV}Cq7tW0ksCi_xwgEZM@Gdz-P^YUA)m8Oz^R}0$P?X==aC=`;8Sk#cQFT zll{(MzYO_uSYtxi^e0RRoh$>1mr@RoaDv{hmaY%x8TYIJ2k{e7{Vb|e>s+X-L;loI z_V5WOhP^=`%p<}Lu$4AOq>c5!b39!ZJh4g74(;n0I*dUaHHaWTbT~U|82OIZnEH2@ zAMtBr^UJlJJ}#<#p~h1nwtFGAwNwSH-xTC>OOQqScgblSB=jT+OvSbxGjQV@Ks21E zUJ7Sx=@Wf=vk%j;ZRO-I%q!koY*cDL!J4$?bZhRl`;K2Y)USOHr5Z{YM#5O99==q@ zg}VVk2$0yxh8P_T0=C88L-i7RqgWQWu6T=~G(i61@*4v;$dYLn2e&em)#l3mFo)P= z7|M2L1u;-3__YgAe9wDGXdx_j!$OHdT=S`mi1H82DwS9ea*lfYA#iq;`S=3!aVsYs zKD!;y+6oEFL~pb&1QNXrz%fY_{T=Pw%Wg6?gg`buQfhzlIGPiaY2dEMtcCD<`h6kN zHnnh0vEheTT?Pv1B58;8uDj&=Y+}lW1Dn z?Zy_IE7}4~lSVRS+pI3%co-dKAfF4<#nN+bA(rHU!h%b@7kilzWb8KN-S}) z{??BC=w4c0SXRlX3Ogs@WfQ0WLN%mkVcy3WaW`y|`Y|4QHdWOwLpU+Duq=*X2^X=z z(;j9|EOVrD9=iojiX-K%3wLgBY6i}V)v0!OfQUV9s03ovUWZnPf$a@We4VR zLFbRS{_q5;b*%>`Wba<0se`Ctu-m)%B;s7g3#y7>qyi593y!AK9{_JD;2|}8KQyOC zTz8_gcg-|no3dOoD249+%TNRY2ux%Jq4_asKyq($88$#3PQ|>8AgGSEvy3&NjaA+m z-sy=Xi>hrz5CBnIITfT-8rd#9=K!`PkWQs9>fyq8cZ@ZzsJqxscWT0g~JS`JF2PXniMkEAs`e+Um}@z zz~7tcnpOKm_>v?&7(5jW-OH9sAd*+uvW6{<9KZG%6JBfv0T1IqsmbKi7x5PsG}ntO zxm=+7X&mO#sdz7*(3yM>sPWEpIjtyNbvL(r#+uPr>7qicsGg(kLA$4^8>VlBmr?sV^ob<{}00F6bMaOpO?nUiKm9~?fsy`j7b5kl~@d4Z&0uCg8G z?vL7$oD7_5=`U^9Pgl$OiO;dJ>qy90H&tK(Wv-XZz@weD1)Yhxw%`yCLptU{*4?le zLl8ngUKVtL!a6JzG-cUgll&4@n~<^usKMxOvjkXlCFJSUCu5Y-yhfpoxdhk>J6%g@ zd)ELT_CFQ_6I4`@b&*W~Zx$jU#8a({xMHES>V2{{za8G9^ny zi#d@~Z8?^F91VUj=}DXP>;(EzfV7_xYIhzs&0r%;Tyo*>=)b##2q8h{;1CJKw!rvk z%x!XJM+-IDo9&U-(!NC;`QtqRhB>MPY#|4B9+p6=HXTc#y@B&oZ9bMj7&AzNWkfYx zM(kq`bKN~}DsJIG{n+e6d}d3-ry1rF$ZWbpf9Fzm!4l{&MhsRzeW2v`ABX3Rs!Ud8Y+lt#?A_VT6gy+qW)<;(XNL72Qp7|LJCtL7^C zi5E$ZR2+o6!sd~)CvMpHBr`eUL^1Xy_yc_-L0@zR?!D`F)3+L8psv4thkFJB$ank+ z_Xt7Fd`DQY=m5I&M^>wtHC-S6jN9;#=Xint35ZNJImp=xk zXA9HUKrOM(!t{NX&bRAbtJyKBNY1JG=o7hRws&0}dwkU1+Pys;zLAJ#>3sa!lZAi- z;4fPVvS$m|pxnM~`!OIfQovNo%Lb_dCiJ>eOL*7Y7Eb6*szp2i^M z{TQOXNcDVg^li%fL6D0{DCc`#unc>PeI*NqJUUyw!&dbjP@6H9&FqV^_BB?Cs`lVMuw@`W zcL1H;^|zx0+w(FthiWrZhY^7-gSTm%qx^%0(TP-H7HW}KKU^J%JEB|wReRUK`=LLS zmWSHc=c9!D18I5MiTQHoED6N(Qy<)g2mV24xf?c0S0OI;3hy&E9gwooPZR(_WR-U4 z?_!p=0OerAD0V=EQ3=O;Bs<_+G6dFiEs}U3?PXX~3Tg9DYjymXq^-$AtA;IgPCb+a zDNp{yCy`(QlL;OC+B{DjnY=Uo+I(<-Cihgt`DP?6q37eoh2b1vfb`CK2)!7-bOPfs6@ZcCHI3kcB?xw0 zS81q1p5gv9QsVJj6jUwfiRV>n^UyD7UTF#3 zjcQ{I*yErP(0kAU)uzRQuK_hC$H7o+ia}h7Ff&qJ>y7#;#Hql{ZM)Z(ima47zcT$( z+cexyD=864r*-p2t1Pzui2(+iyz6T&@fw*PR~FvEDsdOcCtXA=&!bhFG#G|G3|60e z6L#?qUi{1cfp(=0D15<8Wi@xGiC&BoTprCL|)Px+E*5+TT8Qu0Xq0l-h!E7f=Nw& zG9mg9t5cGsOo+~o)AE|6gIL1v#pLFLGK{KE;W9H3%jUqJF~DLG{LxxMpLPr#L0)vN zXr?RN?pkUFYI)bo_m~`BZO4G7(zkCoxdJlj;EhbLi}#gGs;Nt6Qbm_mCiQ)lo7Ko; zfhLD9=a3p+Tm+TA`2|RkZ1l^#3s+!JE8TT?mVeGbIib9pDbs&p0@BKm^rro!h^eE9 zDc%1Cy=>mn?OsVNdi|EE`u~dqY4_|{@kW<3-7`eRiJS*$koHs zQBH{RZqoQL<{60c|5X|j!`GjKcRdy(lyY8>P{Cz+2i{;#k56;1+0sM-y1IVq+Xs+Nlzp`A@edjj(mM@PQx_H^%D=(0y**wX!kjx6`M7=; zbNFH{KMa`hA{&}tX`=Y%Q#=UwJ5!z?4O4A8rpN0A(>*DezP(kz{0m@+Fo}uTo_@P* z><3$qYcwg?hI}qvA}HxB(3JsSknko$xd>FNVdt+L%?#fea3SOT**UmI>L->_;1p61e7k6vWwNPx(m5{-;UyD@kehSuTBxDJrpl^o7=bWLZ=0V>gf~ zMtu#zLhdzqaEm~gL@_J<`D>#e8&L$!?JQm5Qx0tHv0zwRB8ZgHdLwN*n;t2haf-&AQlpQ2U8-G~k zo33LI^Q96v=b47G3GEo-2H;{h6se(G4m*M?3Fr+z2ssi(R>8`eqw8$9$X6${2VGXafI}W&aT)C1xo9A{b50$9ueT zr>x#c8$$~67b7@?bb_QOP=~3@egFEj2|NGx zQL_D??Di|!{u#UdsnWi?34ebI{P}kK2;1L;_JF?pO)O5Hy-0xHpVHWCH@=IF{cOy| z@(wjTVm@fV`vb^XfS}l3Tr37$cApp6r+|GlXA2u(pMM!H2Kwbes_1up3M22pM+@UG zvERP4r6u|V(Z^8UGZD3>AbQg7@CrNp1067j_9w)MQbk0XYYTZHzS>(78k&c0(O1#M z($Ed)s2@0D7|6U(gOz;#TJrhDcs{b<$?wDXk&|@Z@Gc=Mr?m=#IY59sa}v6nz28js zEiXavu=dEg1VgtU>=ykNeIeoO*w<%)z(I>0 z>2Iwn%g~qEZ8e?83vJxr6ZdxXg{mi^t;u7y@Oa;i7LPv)kJ%11Z9vmZG?jzSAy)Yn z1on6NdjQ3Oz7~;`>8A}sW=`}!#7A1e*T#Q}2Jk+;@G8FoS%o^2*W1YJ3+JSX?2k5> z95=x95?a!5gNylqfUKl`ajKX)pgqKt6WesAuIF{K@Tt?`eUrPX&L04gSaBLj89M@D&37 zy#G) zPcy$w%fyO0L~8&lwoC9i_W_@5ucSDPZKyLH#+aS3;`=c(CFX%yCcPbEpPx=1+c=E7 zg~=Uy8K&tJXXUeT&mrzj=nH{dDZFy7`(+P#T?u++uDb?}ygX&V5nJ32nhenjuZL?B zL~4@n9|f>j7n9o` z+fnQ%wql(h@h;)jcS^m?123mNc*=ahd0=ANg9`Hj=Yh3p51ut2cwoR`=vSYMSt&(0 zlNXCjJ4IKK)ZIhdXct3ZBtXz7bWc^rOK1;ebc!uQA2jNTMyB!X*-4RHIo}fKD`@DA zcA`KhqiKc!(R06Ln=cAwmb-t|27iJHeqF*g7YcZ%4gR$x_=7{*1HaG$KQR^j>o)lH z=mQ!iB*6C+@cg}~nEtvX_}`;xTk5xMGllchh=d7ri$-nmGfnV+MI%!F_=^R+KG&jtQxg39r?#O!)3#;cPD0-l`xt>m4%FN^ zx(lFUWrEL{_g341`_mQ(lG!fTF?gywF-s=~?mH&Eonw=Ph&BUv4f=B6u98lPwa05% z_6Y7);vRv%(9}w_H3#lB+bPO_Uu*IDf$-XycJ|F+@|un&LvzCG9P~{YwmuVI*XS3- zM_MtO%E_zNoEDc4L!HTMH}ZNcCh8QCW!qrBBA6vYOQt{mRyf|SzbRcUoBsyw!Ee{t z5cC17TZC1->Ec?tjjVPDxnh*x39DVnYIa-jnVbu&i?2zear#Yy$`$>EG+r(EoG$>M zu$nT>K7u-v)tR`{hhjobsMRp(b;TB+(pKJ}|8n%8THlvW3AOI8vv6nU1NU_FMcWZ* zYpV6$t>kqe=oMab(a0Nhk=I`Bpvh3{Yh0HZ%3W6{$mB#@Yhvscbra()g3%dA#y3;3 z@1ah(bkA^ z7Ar*`NZ}GRGW|s4E%MR-YD)@zMGD<0h5UBGuWK>E&r1b=ybb=tA_4y(8kyjS3i!r3 z7I;q*{0nHBL4Bq{Aww|%N&>y-LKra5eOpLuNe8@FvQu_dn zz|TWKesN!F%FjccHxs8xbQHXbXnKKw6rww#C`3=+1j0_YiLOEkM8{0g+=|Zn zz*M3~6YGm75xhE}6IKtKV zsXBf#RVcKtMVd5{L4?3XtrH}`Vd=%fw23>gR2WB=kR6tMA80*jgUcizh{1+YaM;Aa1JEN%Eg ztViE9)|Rfj{y2e--H%&TRZE{a3cWBodq-!G*KtCP^Ux-;CF7d{$0gt#Y#RF6&SERy>90Bnw$^G*~bS{C0eIvWou!Ps;(07Z9P zfs4FrwTyS+3I^-b5|tCpN{kAQ!FET3^@D$mhVA-KLXtI)TxX;D@x->mXlFd!z8)J4 z-*75O7#4nP`T{7xoJ;MWb3JK?G8U4HV>I`-8Glu{Z$uN8m>L-`LVPKgN5 zLRYA8NHm0=u`xoG>l)#-Uwl1j96c(^%ZNvCF_cR{E{8Hl`n|$1Uv<$i(&e?D9f^_t z<+;Q|U*@jQ0yVnIt=66K?jdtpg_D)n1CbJ58c}g zXIR#Fp-;x@iY99Se8RfK02sInMHv7yK)s%QqBT}|9v}w5i)nxOvAm!z*qCZz`bo5n2KKQF>nVETO;_o zMvH-`SeInr_b3Vj^WOxK-V-baYES|LH?eU<8II2Sh;#;~gBCJ?7nUpr9w7s}`;&6> zWCduV^$qgVX6BB8C;N?J2;&&Qf{~mSeUu>X1c>&yc>`2f+*}R$ia*L{U6PwD6os37 zakrp zHkR#tdOYA{L>ll5nUe#o={I6G-RUW?3n70?I{9mD@;`;x^p`<|L`-i}h!N+*Y#dR( zfllKV0S1(CdUOLp)B{9&N_cTyf{W`QRpDZDIv20n zTy!88S3^Legu`ovi$9oL3`b{u+DRlVVM@1MV{@^YBxI3<r zR$L6_9!M$qqr8aH`7x9+i)Tz^$(j3DNhWn0#&uvulzpFxaTXg#l&8>H ze_;TDK~ZMF`@w7~YH@M&UqpB@5c(PEn)BwsOB3>X5$&R}q`Xc=%QoW*K~JU(LH;-} zYLWk>EwCOSC58N46oq^Z%Gdt`*rvZj2?G0)h7nO_Q(!+JK1d<|IFtM{qCb)R-xu(q zYl0x`x05)u(Vo@3j989~p_E<)}+i;&|7DUV#J)*^(F+Yf`Zj)=F|a2 z%bf1L3gS7O6pss(WQwAlISp~jkxMwdc3`6Ke;Jg|72flx0$k2YrGQI=7!u9t4hB~X zCC;DMkRXYw{`jh85Z{yOqzUdvQ6$-RpfC-c*34Y;Ki{PSkf{(QcE`y@< zUjax69*zNjqaYPw_i+)Zz!LvaFC0r6MB*FU? zA$ZH=B*D+&-Xi#H5`0pU;Fr<5J%abIP7wT$S1bv?OQ|ObZbnfEKJ!Ho^#^b+5-vgs z1V6*Z5oG~7>yiE>DKVd|#(IPVkBkl@!KZ@+m_)(kB*Fillpr{a#?j$Pf^*QiJ%Ue9 z5Q3&3%3PimFv_kMklwiZpv2jE>NM^S5O(Xb+8k6AB z(Hasw4g1Ajp9pD#=jd^_$ff_swS`DkMK;X85L9>H&bN{isf zZGwx)L{h@Xq9_tR@DC8Y5S)vI_oGBVOaKw(N?i2Wh-*?z;_exygwKuE0eh?q{(#^g zs*(i1i+hXUnP?onIZ5zG`Rx9Ee&Em_qQSD1qQR z*f^pTli<~T(g^-!x=CbW2-F}B!qD>2#Kyp3WK|xZM4^ZY!xSarhFc> z<F4=AkI^S^6wwz_q!^=X#VNpRb@-urr17c>r-&N^BLKCZVA>dN0LO3ZkIT z6G66Wt?4`9-qPp2XdAsh$%2cmGgdN)14bpuJ^de+I`b*%B)M5A3c0nAx&9;I2)S3F z1adzkfQa%FpZ%*Kjof^b+%eHdNiJVW2Du9-B+0!5_ZGRIPX)P;C&``8)*0lY_cZh{ zW3wK(W7eKmz+iUrF~C6NJS6&e;_C|tU~me!vh=a)d5gijSeImQ8H&Q-xW9vmV{8UT zq67x7VB?6g5S{f7=?tD`A}o)-OoV@T34`}k5}}!6xdHbUgE?eyb&|owY@NX%Le5ix z)XZ5v0fc05xCw&{MyCjar$*NhUnwADq$}}$#RVin^1tj6ksftwl%p{AAbLO~Dj-`T z`RF-IBmr`n6v-?UMI@7Gq6Yej&d)PVbet)Y$Iw~-u@`|QRxf=`64mHNLD-cKl|#>v zC@f--`J3rah~&U!5Xoi{iRu7(Jh;5ANSuEF8jIZNHn}dckRXZ%R@~-?lN|>$bETyf?O}jeJe@s>uBAUT-O!EruDm5 zE~7t$#Ne)mKmr)~w>?d-r9uRD{`=H>c;6=fZ7zA<8^m6w*Lt`!@8Pv>1x5bwD}aQN z(7|V&(w=;^nClii1OxD$(}3PkzB^x-z%M=eEy8jux+}p1j>m}Qx5I~_99K!g%=0Ks z8e}_W6#65LFfIB41vU;{VNi$u&Wo&mMggJB2@6?gX$ZTsSE48y`W2GUpGQA2sBdT) z5$Abq98nBh^e1|f_ygRX#ZE}St<{_pmQ0I&2!tf}auI|x#4aYz8i-nOzDHpva1Lxi zQE=WxoM+oOmzy~M#>Q9=ptC+ya3;d*O9bbl!fDZu$>p9-MCI(B#JI!eGLnXI$Pydl zKw@kGrNU)z6Jw2u@priBZy@eS**f~-5^}kCT67-~UTz|M>~9GcU%{=gxQ%rQ!|%5a zMPYF&RIXpr*+Mvh1`u&R%El3;4i|kq5OP1+-dM$Ptlgsffvr4p%e2^@JU(>}Wd~EJ ze+RdMaX8sdU>q?3MZtJBF@6C`g~d}%j7~O=DD!dA-#>=Ha83`;K7%&9ML!pGOQywU z3OfD@sO>3pIQ?#`xxLFfeE{i(9X<$qw|*_(E&8PZs+$(;13>;krKx9ZVH0`KIX0Vo za30X`OEdDq%gS*CCudw*n`B?vuy%AL2A=6y0#OWr&MZ+BK`J7Ov%q9h6r+n!6j7Y^ zSFrm5ND)z-fD(k?nT;b#9B}j`Y?xwU2rM^5dUv?+i-IHYVGk$H!b`2M@`&|psA-sQ9 zE+c+>oTVXW0cqORM>eZtocxe-NC*HaLOeiQnMl|6_O)O9Hv zN0h&zvp&y7V2LQ~feVRn@iox_M0j@&AKI_*d;lYBXS-l2UvU(d}U@1JO&QQJu4BJx9U%~r!A}jpDRA202FY~Pe ze47FHFq0zm_J^ORc^v(Xn$P}(upCE7xUzvC%-cR4=X`q;66i$+PD?Vt1J5xlrAok1 zUK?xEz|Rx*+kXN2oJDE#JOT)4&|`egJoN3jGn7Fle70T9wj_6@tfBd;8gHNDo%;{ z{YjL9%Yk+TKZ@s?sm;!>A4hBa3*zlZ`6f^NL_^t7#zu|&)Sm0rb8#P0<^YiVjQJm< zdE;Dzemq&G3TFk7xVr(vIwT?W=gsmHYmO@W{FfsDxe^^OwXGj?3qGcH^6BCc?<^|x$d9`bOdRpS)A0=L_i;V~K;y&( z0o#JZSFv)ydMANCa2W>291ht=H3z3DezU}Lm7M3GYTeYx4LFqnucl8xVAgfLYBZ{) zn-+bEUsxjmRsMbsEYmp8!?4Xd3EMtL(`!sXb+bvkc^(f#Tu4$r#O9rjlXEiYx6gO4 zQSsp#pLPJ>uqlwEFE0h881}J1vB86%@T+iq?+T^z(mZ_UD&mU6OBhPOQ5?^Kj>V0! z@1Uj7(w>gs)X-AT*x<$ZVLd+CGZW{fm2M99!fCnUk>BGfOt0eD*jNMFGyKN)SM~<* zULj7ZKx1ENWAHe9Hn4!l>!RsOG>uQY3c{&JduneordY_eqKN(Ay2t0Mk+B8w4nD2P zr^!JyRe1Z|j}TPF#ET<1$^eHEX-PQ>!{ktoN^K{!=fSxrr385xyoDfFEpPde79d&T zj9j`S-l6G8cBHQlzstrWL-mZT_AKs<@3^A0xvUV~n*9-+)9uryK=;?;EbN5{H!}%BcAzDNE3HAsDnDYMw1&DtD z#6NcopZ@7=5&to5`fm`B5cXD{+UhY}SbyA417w zb1&IEm0c}1htVh^GOr1n1;QrT)7m%&dNx1IuGHBF5Q3c{_u@UV2Y9A#sdF5v0t+$H zzkCP~OVl+sooKtsm4fu`a;?0Ex5k?FuV{^7N7 zC@xQ=fD&krKY-RLG7{bo9%mixB&V0}_!WHc{0kc8bR3%E44_#MQ{sjB+WGkhoALs^ z_#r^7hkF1;N>i;Vv@bH679RBS%-w@c9$VQ{3Jx}9%jxLoXJQNF^K_bBDgDz5avqh$ zOoq}0CU32`d(yQHE8c5r#io&GKhW^G9Mu=g;l^#xVqr&1n^r%|zn5Liaauw#Rq?A* zb9oBLS4?}=__a@YH1Mzr?XdPEKCRfWC)k7MW9&YQ6!F}I!1;`IHP<01jFJBw2f6Vx z%%P1XGwAZu{)zujbw%xj+-pi~hJtuw(zRTQoeX8wnPOd>Ie#P15#>zyLVM;s5l`C= z5aPpBIhRHUk1JCpB=60`GO5)0B|u}+ln+n5AM8iF^w2Uqbl*kgk@K?k@i_D%_5wX+ zsq+w;2UW}XQY>tQEVGEE$g;H{oXWxi>!4o(^wzf^W({-{JBwe5L{5z)4ro_KJa~KI0BqvY`#K`u*m#DK{pKi(|!-OabS^Z?1RjG6mUfO0HeLq zJ`ejjgs5=-Xk#f_^qcgzKF)0)00DT}%|m5yPS^wVrz=NLbFOz#sKl4v{NWyIxFgx` zA@6)o1-pzUrDZ(y5CTn)LzPy!L)bq;{J>o3hFe{}^8 zcl`w3YEA|GkcDB?O9{V1!U$$0Pg-9|8-UYMwI=lsYVmq?@OpkQX6ax$Q$?#Nzz{ry zkD4AaRCh&dj6oxU)6}~7Qh3!|w8*>h3<%P@z%aD>J2u`0xH;>MecH6XfTA{icD&l9 zH9j$|2*`2xF{~#Ch?%#>aj+ChlR>Ywt>w84eWCs`oQ&{m%k8ae=pjM4$^perFO#{u2`n~MlLA;FAaNd@*?w3#zr!; zxeIr7ikj0Zi^aFS#>MD*ICdV7DbifUz$3HkWw=2X75+u4-bQn~Z69fisJDT3Vfxtq z7@P#JWKnVmfdMo%&kFKl`(-k6^=6BR(gL@d5Zx`O6A~mP!v>SuRd;_KaX9$j2iDat zZ%rxNgErNFE~kwpKEPKEH<$mv`vJaGub%WDw0~>uVU6c@)*F6p0V3Z8$YnMnoP;9W zcW!570MB1q;I97|_ddoEjd_)&n-|5A!)k>%aN3mCOD+AtUB6BmkIf6MVPe7Ij$Dh1 zLm?^f+g$Ziq`{axZ3XKZw%j!L(5?2F#>#mGhY1l@Xf|79p(RwxL(mB6!g(RXn0{En!-Uhrw~)Lxs(#Wy5BKE_Y{ydStT zg^yE&k3EdY@a3n*0>V*8=DF+TGh_zb4SCtMC>}oz6P>+YvzTcLxGZLNrp99KhC#4l zpV8zCt%oAEx>sIM(eBo|>eEC(o zIS(J&z#xM{%h4`824&I`9+MB1@k41mFcy;wCes#vtERnAucXCM_K1TzCJF_<@KBZi zKg}dJ_4_*}x57ncHN=sbQ!x)?gYK&7Yii87pV63v-V^O*x5|&1Ye&4=ZjkI{-r&ho zDPjg9!7k{f%5oBCzF``zMh6BUx?^itS3G{lp%=^YLv|pp*Pr18%pFihc@m#+)TP;4QeJeiY z6+H2UnlTub;SSMS|AS*a%K}bqyaxvZ(}v$4f|+)_2L{m4j21XRlEXT1oj-C0KlbTf zfoRyF?QZ%iJG8&!L97G1<1qI?2Y+PzFnp;hGF~Od@oFi~%nTlf%qvs&?tkp?yZ_PX z%h~4BcI)33A&1r*Bb&>3kDo3CYYv=Lbth2!w1b@f>M(Xdr#W^mmU*L@-zZrjB$GUgY1ZMn9~K1(>s}E>##sO%7cxp!VtrNQfYRz_#oU0nUq{FKxIgK zKLJ6KK7J5x37!(*JeyX`B5R zbv3loV-W`WM|O7Czbj(F*~;)~T*-WeB_qbo$BFOX=DX|9r!PmR$ycT3Upo0p@o669}ov2fp2g@)6CL7EuTof*phLMkH&1a03u-39j~)9t@m+w~By4 zgVF#J+dG*c&m+iIyr}|^ci13zqn!zIZ{P;gT^Z&sTQ=lH^1 zo8t%=>hMi@%!r+aI-`lv%*Xpo4qVO_!rS3zSQ8I99% zdT2!1E5VBEWyl=L|I1MDOVjC#j)}pa-bZuYZZ@uC%k9SGC18YPf>`RpY-oZ9fIj&KIuC(DbO$4d*p; z6f+XU+wE7nB3Rm*9dsk2gN>&Ybg{opAozPE<2%@qj~NHnsT^b$x(y^ZgPLMHLk z(Q7Sw1*ckFeio4*rAr;*5f;Dg&@SMX$$JTwdt4r-9h9s1r0k1#swi zo%gW)$g!RP9ZnFn2&zv4TiBtwj*n=KcQP?=Wr-7$9o9&v$;s-rYfE3X1I%3_9rS+_ z{XqnNY}=D0Mf&}A4I#tn|0GZ9UIgJ;z$QK!4*5CsMAZL~YB=|xY5YFx{9?_kGQ7a7 zhNS_#2H)j?XTeFtpAm3)tjcHoC=woU>KsA}t}S|qq@tL zp9u}&MEF9r-IxGsOs;rvZ{&&P29$P}nL|LFrNRSlm6m!9C;%{Oc zb4J?71M_iotmZ2LF}`pWU}Q!J7A27O^;8nx6Gaduc%Lb>T}V2q!ZM^+CRBLKgn<7A zezYb2vj2wv`+NE#t)XP9&w4lD6sDI_7!VU|3h;yI>@5s+mFQdT*Z zAg%sMeu5`JC?kX@*94MCokRwt$|g$g|3nXO$^W z&G`b6iS7Lrcj@eHL-GkBoioBVG`R}w&!o0Vq5pJ@jadQme>9ble{{&; z-(&I*&4d4!5%Gy7giiSH{0kz}<@{gZKOq+JPYM1>)6w`3oC|hlp+(@#G7u&nblXeS&X z3FZ_x!UrRgMQWiuynF?4`@`NMI905^e8?4gYiL0D-X#2^*Tbgx`luIMYu*wcmUv-r zl|Sq&Ls7+lbJz?`%2N7Z2sM?5y>LnM4{O@`%;_t;N7nNP$uun*i&%7+>eq>8)d6L|XcCt$+FQu^$S*}Qmtz5Sk=dv)uR4x$!eybY~80+e{NQb;!~=(o7JNA zlQIr*@o4Pxt_~6Ke zbxQ`=`6*FevWF|FxzDH}YRt|fmoXHV04uRk!N8^?w7}oZN&2QKWiZlTXcw0Gk&%zM zUS!m441B$Bs=C^|Z-DvH!wAraWG?r?W91sYRF17+Z)m^Ui!U~~`HMIQ?y}wW51Dsa zfr;MGFS!AJ+u*)_z!FhjAu>C%>uvEjWjrjo|H;Ag_S55I#%>3!7LV zdjjapcs_n|viZ|i^9$G<)xq52J@MISc&LbxZ3XCaZ3A1Cnu)VSaK_KC1mFCu?oQ*~ z_-KN|{y(r^YJv-#C|x7Ur9!LgjuZ$7`;h85)-z`J%q$-!7;DJL`iJ%or-%1V#OZ0` zl416RNb4hocLd+WTk6-wfX*FjVcb|}Oa=lgUtY?21nKg(SOuY+mboARci(~|@cW+d zJFZM5dyz?FDfTHPy59vx^&uRaR?Z#ju-`n19|JqUiz8^cS??s{_prPZke<9uvXvOn z_&x$dCJbY44Y?j!L?X8YJ88(aZY<*04rB)iyFr*st}LwbYcuC?Q>fanEv&Y_Mt@6< z+B~)feB&H|vDMaf4zG9*hn&d^b=;iAt?L7#7^xNnyCNn2GYYXVduqySb-c7dkeD#% ziU1F*VW;{13S=p;WC^<-MhCwB<@)g!=~OSB_zpmub62iUqP|Gp<6ChoyNuof(2Srf zMt5XI#wY#geg~(P*NpmO9! zf_Gg8!VKj@K|UT6?ZO&Wy9F|XxG>luvs>UQx$hi?BsGY%t#fB~AioV^=nXG;l*PV^ zq@Ag-glgqR?6;zLZ+YY*?E1X#)izW#?5QiLocUH|&X)5^%eUk$$%C#jU*z%0lEGeW zy?5}2KMY>K`>+?=PBUB7oGs<0JIl3Q@eWH0$|KcT6{R06dMD-Kl5A{s2#|F2 z+xWVVryMyrD9>-cDON?A=I}clV92Yr(ymIhAMm{gNE$y=1oH*VFSh72@WKT@b{WXS zr{KJYTH`tREE)$RY;i7MCE`oI+{5Qb-n@9*bHYtf0p8Oap(&-j)wb)uVlCz%Xt94soLX!AAs;h# zJ}_V71M_n#A=~OovRnxP%jB3IdmQae$aYLq1Pq1d9{M<*=&2(|&v*il+^j+W>*;@Q@#GfPoB&;V=BrSG>%8$5;C9l9N>JEw$;>Ts3E>o$qz$ zdk^4~PHMz=z}+3wZ0Wm8zpl(VXlx1(;dc_ogae22&JXuJg!dWXJKjYV6J{EI@qi@H z1-%nkT`-~YaNS%(`K1>}g}kUD@3hnK;WkPbI)G*d3`fiJL3ajx7}HIdnjRCid&xk>?@uJq<}>;JL$#ag(w& ziImxLdutM;T3eG#Ku2T2-Q1TPlh`lBz9iW9Vjd@A+3(ZB=KED|1&-rH#OGMYJpv(k z-S;)wO}(=1k|?>??!#tfHfdu9k6W@Z-4e!S9&B9YLk{jco2dE7Wd%&Pm10+ME6Uam zt-rLHJG3Lt1R6NYipcmZdw)~w!PTR+NxQf|;*I>ZPd;jJ{v1-y-q&&<2Yw>0 zBd&02&wFTutxUj{cq13$D{qI<40!z!tTVAy<{u0b*=oth>t3CkSKk;IR+1Gggeg?QJ1({mBO&7&3-$-f@SIk`RUfs1|S!~#{Bb{!1mtRBP!rhw{<%M`T zgxffSTOmIp9p=wz8slEwGiTW^LxZjgSjBM`vmGO2c8%(EBTjm4@~*&)c_eqy4(YtM z1A=JDONzWV?4lL;{8t<4ECZ8yPn?{Z_&R6kb3M`QCfP?>2Tc93i+3n zy_yvo?VX28cf%(9ZFZiH0-!ncMl^!DFVJ^(Lg^QHZv6yQA$Yw8GQngx1sAzc_G|4L9!Cs_%jQmDL-;bM=BeK!OE^N*qk$w&?xw z2uKm&ZeGg{@=I^{PuFVd#n^2)f4&Oo8xn}L4Qx)rUKt(9cduWgee2us_ipG_2i2p(X5m7!UL*U{fUH35`C5Lq&{}w2H$OzV$0f2xeksi&lHU2ZLDMD3R5Y04f8YFHKGpOca+Wj z?So+SC(VK#JcEali$jp$g39QKkif$JZrJ$<8i4(Syo8-RSIA7sg`L-;s@ds94be6` zOL0eCHajb%*V;WC*ve z)zS^YfEt=r04$YQ0p2V`Rbbsvk&pLyQPLXK(|KQI-bX4uX1RoAd|f_L39q`(qZZ@7 z7+HhjCe{4@CEmqQ2cS@#M5VgRo1lqJK0Jbrsp_F!C_c|3G>;u-_+c6qCY90r4xA&{ z0ly!?NH^ySPPqJ5=5Mpn!5H`@9>AwWT;HG^`%ec{Mx5W{9y`_674YXpt@ghtpUK`x5G`JzbY$!h<=QH z7n*%L>Z^0%0gu(EpbWQppIMzdyfNNUkD#zBOAxXTod0N;8Jz$9COjL~QnagY46+mU zq|HJ{{c7CAzfS3|UxFI_BK)x`ZJ(NvE0JI29N-v@>D!!SG1x2gYgVwkxDHj@jx+^+ zpNIK^%dKjxM5?Tjn|S3)hVOdx6<%)_AXpwpTx+gk9p=4tsMFsvp*!_gq$vbz@%fxp zjy;JRbNstkI~SsJ!mz@|uO)_*ZuTthL=@ z!df=E2wff651)-p*NxZcUqA`aWD$OUQwS=LAz%f4!8)n#!?IJ#fuWFc@m22YoIwg8 z)MEVTUh4dG7Vfdmz=cI#QT!2?&CYfBn*!J1DC;Z!I5LRRJ5Cv8D?g$Wx<%_AhCrBik8wdbaSC& zG4DFVE<50wEoNGEGH{*W6ebG5D8 z2T7c(Z^Xz0-I2-z-Zi_RDE$(^XA$RFqo=Apq64fNxxW<;F*m>);(Y)<0UZ3W&h&zj zeezTqB|RYqdqn30`a;6wjfWG>RRX%9g%9B^tJH7<(U1_S^saf3SY@tgSbM2Z`X9S45gcq-K=+EfB-}xsBcrdxh1ta%@(&kS4o@eq5S4ziAZCtK z=n$V~PwjiI*|#L4Z{AV*u7?4$e?H?>NwMO0Ie3lqiE^!psVi-dclm+&fmw6C%YV3f z{w!a)wpBZTWn*M)Rz;Ux?kBv{+fk7hi#3Gt`n*9(FFkZM-AuuEx!pj%ZaLQGYExu;?U`4MNH_1~9oc8Sb^8bz0cD zq6;?NoM)m-v$Gmx5PEsoH5s?=wg2kr4*kxoC_}~Tuv|yD-$?V>aSLxki@pmN0({mk zq$oUUmtE$sHL$biIdQ|;8-kZ;&Ms&jYRPMM{xZhl$RPmdVcdBSZtw(3LM@(V=cgz_ z*?8~??$)5yuJQq`<)y7WQw5L;_l?>YbcI}vcv=?jw;C5JxrlKwcPc^~^Ji?3DG)J4 zapOqmvuJ@SB(dNz2L6yuY5ydDKbF7zqP#Ki7rFnp{QZyoC5gtsKJqtL{&v9MZQE95 zZ2)X@UaZu-SmVCMw-8;RJ7xA|3{#<_%5&ko}^VBUicyvVpa(8G&YP%CFv%?e+KFn9z8^Jr%fk)H z2WI23_|1)3H%xx9k)>g1z^Cx%Id}!F^?T&Gw2E47GIEraHG2JF6Vfn@VW40lKdX4l z@)9Vz5jhjB3H6ZO|0n-l_Pxp0eCxWxx;|`O*I3tgt?O6T^*`3N&ktt*Gpy_R*0sjE z-fUg}pZ2~7IL_m`?;Qz%N01aj5~LtnQ9jC~2)QDB1Yru1^#>$K3bZH+1waYz$o}qd z_knlf;qH9*fdmvy2emmBS#zz1IjwDDTvSK5$tYRF-MKg{HYuFl2Z3U6p z1a+kZY19rHD|LTw_wD^7ub`*Nw4Lf^mcRXW-@bkO-rGOlx4Ynk;8z8WGAsUC$KI6m zKNI|!q#L?Ad45pvs9;#|Zox6ZHw3>ZX!vaT&pP%o!3PA72>JzG!9l@c!AZes!8ySN z!M_o#d{@_F3Hk&d7VHr0790?KNpMW?n}V}~?+AV%xb{6=&pm<<2(}6O1!IDz1z!@p zEcm+M_XXz#|3a{m-<|QR5&Vmv>E}jy{*++1;B$gs7916PMeq*=elvFZ+ws((_@@WWJB@JsZE0x>rdeEOMGJXRN^!si6G(TJjf zo~}?pb1?aJiawX4W4p8E^YeFSdDP!DzdoD4=cf64kRM6KyW`28fUCuz-dH3U4>@$O zFX04TCuHrY+r2k+pLIO$B3keBr8Hroyg4o*Cm87oL~SR=Y?4Lw7UxnnKU+ZLDCBRR za#X?IlpF7{Z^$qST2G~@uoE5WaY9bi4cIx^x$465H>Y9~iI`b9jCFM~k#z8{6$}Sr z#~mw#2c^PYmK%jjRa>PnH zsotn-or*xFUe|Jx$#@dB0qqlt%x*?;%@5Y(#_9H3s`ef2$l9qp%T8g;()7ks(MZq< zbVbp=o8)UHb)|yINW#@r(2M5Zjn-z1D0hJuMPebRZ&xbvtac&iIGo{`cqCSPuW12l zb>nfk8Jdc2o@;K(O``d_3F=>?kR5GS3XP72VB?bx__)1EsLxW>rQAR)=veV?Ykwf+ z9PA4^33ieDlzn%!F{5N4l5!rf9Fu1Sqk$BBAKjH5J^(l9(n-Z!v@lLzAm~Q>N!*UK z8qp#*aKcGp2x`PX&xWi-JOw9<#{y9+kUZYo1INZlm9lC%^LXT>6T4S2+>t_A9=v*B z(MSTOKN*Pjf)lIbgwua2ogK``#N48#}$u&W%E-Rnm4+Ztc95ZT`x1 z80h%DG47&PZMHC)poNDhF$c=;i6vd0)5Y5EhV{#PZKaee{YRvlOmr8ldhr~u1t zhvGpcoR%*h#1N>`S#6SG^qr&2>6O!LjN{Sn-1x5fnpUfJh$`2%ZFvw9 zRX!Mvr+Skz56MG8KCc|~&$fB~FXC?$r{psan#Q*C^~&-U=nc;@J*gRv^WomT!E6l= z2K!k=b`@*KF)M)83jevI;_)K}kCe_{I96o)yB=p`7*dn6DNr!B><@(0Bw}?(1IJT5 zIoNhJKfAf>zMF2sWvXB;~cv)yDM=yYMg#1=i$;TOp`TBW-B+$Yy6*bsi4og#vCs&41v36iYr7rJ(S~GB3OS z)b9=YJ%M{%sg*4FDIWQ&*f`6h*WnaN+1V<&E;sICb#An+`dKsz{S~jwd4*%vARU-J zxGcs&=vyy!xYr)&P_jp{aDwtv0lPY z2!M$envc2nS#eH1g%u0BaXjEwDlQ$oSm*Ux%IWTnV!?>@?J0*Jt!6Y64#*2iQv;@y zq=SWj3Jk5IScoJe!P>t2eD^9|D2%B)z(Vly%&P6edQvSILCq6z=w7J`OJ^lMPhRSE zftT1K7Pn2WN!3WxjqZcFJ;?*XF3he8Rv%VZ?kQf1YY)UwfJIUunR4osAW!rrt-~Qb z=c8=_yjpPA#yHobrZ`qmBvq$1@bJSAV_q$Ik*k&V*519l&Ug2{x^;-&N`Wl7k_5g9 zi=p2_je6|Yi%e}t_GkSDpr2Vs#uwNJdLqGOJmqtIkLi2(+`A~jgGJPuj)d&~kYrfCg(I(W4Ci311Q+U@f_LG?>RHn> ziHtAnQmmg?FOOZtuSWXCMRf$!WbHKck2(GM)vA7)-+s*Fynt7R!WbDg<;-^i=*KqW zDCdcrC_YBdT^Q3%IfG675$EsW=Oz7prkHi+s-7371dH?O?@68;>M;p(S|0Oga8l%( z7Mu~B6`T`f`b!UP2{Nuj=)^WlXb$1~obt>6YEWdwz>0wy@VF2013(M-o39eB`CY^j ze(+7AKS%i1?-5FpmQ{QPwgeTV2%2vkLsP7}>eV#8!D(KV!f0m)x|gXl%zqs<*h zn8tsZsLplz-0eMTe&zo+b}#Zx+IF8WTSlk#fgw?*3^Wm*&bEI7;R8~Z&5NIh2#4kCr1^A*hvu24ONM70zMICbUtK~7nmpu3sk&q;8QkvU z&zIMl8Muo`*}WDL7#M2(VkERRn)KWdCELgwjUBTd8x_u(kDa6>yW$ym2}{` zmmWdd{Ya~9SWT7n6$wvu8C83JmDVNJ`nK&@OFQ1bgYLS%m2SWG5vsa+Csl@ig~~ea zq9z<~>piN+CLQgxQ0^KHVyX>(CJWVs>jHpsVu z2s~98@MIbSUv5M`Ur*Hvs(NMZ^fu=Mfsi=*3b*@SJLTg8)@C}>iJdE<%u%?%2xgZB%ZC6 zwAEcrTSH#j+OdkZLY}SlTjsSLHo^`YUK`19tr4f9%d zFIoQ#V++E(@Xdc#_Ucgn@87nef;OOERH7f1Ll@R>;GGDe=51~>Ixg}Ed2~Y_70`M% zd1+I}TG|BP+0?dv9{tJhSzk`;Kfh)XmP%Bxzqfm-9skGC#%o?$hrUtXaU1zwB07#R zpz1?i$RqxLkdRMB`Ko_^o^1^qL)WnHV)gvS#0Gy=t8Nq8QD33$2YJ&NRIVYs%e<9M zSE=WpAZ?ff?GS8Nw9Hv%1v;<%S7m-p2l9J&a5sd?@>OFlR(ugsQSe!(akzapC@`1AUX5 zPHgfsUx-Y91)3jz1AiA1d>EAsZ7W(mt39;33(wv%@+Qj?EB%-k*FkR$(0hGF>uR1O z@5T{(;j|xh!Ov>Egikl1Pj6h4U_G!L{9{z* zeCn~_AKpOagje1XI>ebigXal^NrYC6|9=R?Xsb3>6MOF8bqX(%fu7LbhC00UP;38T z>nr zo}bZr`0)IU*29PAXS5#vZ#h5nX-{kap=A77C)Vz`Hh*;@s>Ybf-`({$BApm0!*&LL z#I}z*r&{p=jdz)8U`2Kd9L4v!{Q36E%GQ2-eOKE(>I-iS{Cn_aSAElc1iYKGTOzu? zX6Djvr(*a98FRbo7kKUn^J#*17oY#^M;({O%Td4G9Cm^y+MFQ1+v5xKkw{O(r5}JJ zK3UliKnsj_R@A|}O%U~9Pr~h|ZwSq!yb+w;r+%C_DDZ9Uhm!kLAc;(@DOo3G*G7r> zq|y>^Xo4k<&A0X+!RDvEFV)U^uwA0tATz%9b~#D=K;$I$MCb)j9ahI6Yz^4Pz-VGJ4aL}P`oM*J1AGsukE@*})o}VJY$<)@ zLF{ep`mKD~SUD8P(LUO2AB(X(Cn%OnZ3(2p^dibBFVSaZvIgf}f_KphHvW@(zlo0L z$|6;J+ndTSc;^I?^ztU|Kj60WKCKdY7+O?r3waN_PSy`cvgyz|Jq0NaCE32(BbQkB z&<58L!6$n2DfVMo-VmUjPj6tgiZtWz>>Y23-B6(@=R|?rkOIBrr%Z(pr8fV=&I-r- z1f<|vpX3t>Vu8#Jet*)@r%k?YT5uF!&S+9p3BwQ772!%u{Dna07M_pwm`RZ zO7TC0U0$QW$5|J=`ysmUfRlDzR;cmZzXX+dhGO(xJgYvTp6-;EJb|r3?4+F1jkz~l zH?+sbpf7+<`UsMAEqbTyZd)$oM^ZI-=v~!oENOObIE!-m{fX$eO^RyQ z@8{K}J?t~tnM15@KNgFhimBe8%(dun>M-`w_&gEag;W_!5A%^GqIl*C&BIEyzr-8R2J;Hyj?U9+u6 zvpuL&YdSZ_?Tdxlc}%f#tx)6P=khdTx?|Uko~0_=CGF*g>Av$eOXT#hD@1V=kKCLz zhf^I%?4{R88MULXc8>02|4}1htJ)tQ%B#)z)Q^PkSt5D{qQW%S` zU|CFS^q|$sE24xtbhYRHx)7fd{I6wgZnQa&z(G0(`^UWRL4V6>N8<4ly$Q4MI2gmB zDtrehN6wRxq>EEh$6|Q1#X8{6%8$ix_$!1>-)yHngyWMuuZ)(Xua?ZNUjY|y=H*1p zZPjN<>1uh0lf*V5R$3+r?L3^4hj{Xk6JS|+(T8G`DEo)KPO`rhBRkJE=t<4kc;?FY z+{pcKi0;oz)yoegQsqiWqFto}8_)YvXn?~pYVaJuNl_e%)Wk<3T}d4AVuPjViz~T% zs4Y*Yod?qvjEt0qvA?HEwCfn)5au|!`f{ZPwkLfUXB_g|eQv69|S1{@BlG8Z5qPutP z-lZGzAh$87PnPI+6aM#)K4ZPreeD;&Ft^Cfw;2BZvO+YQ(s6S|gvUqUa!Z${*6 zm3qsw6-q?tYDB)4f2a9j7r*c)be8a&7Wpm~If8lg-mTmFwD2oc@3io15q>!RQuvd4 z&xm}_i9PfhI7e#qLPEE2O}7KbnF@cVy`n##=%rM>vqI;Uc9h~bBlWh5Jxb9vN_+jw zw6{6g=D-%{g{C3=3jq`&BHm63LI3!Sm2>2&6KQuv)Nsb^hcQRtSI zv0JUQcS-trDZgJ5x|c*goH8l=$#~$CdY@j#uZD!KM(9e}V^HYQQtzh1S}`GM&3J{b zQS4UAucn3WInhfgyZOYvX`!nrQE#o-trVRZPtOQlRP(to!W(V70? z75Us{{B&OU`Gu~OA1nx6=Q4V+#9vyLX|GrKy}Hc!QX~APmYI(lMSs_qX-BQl)k?iM zU0V2)el#!on-hO2)i0}r-<-5}T_Lw@znl`fGtv(H+53W@v0IJkWm@=^s<%`4%`D^3 ztwLA5jNPiFy@_S`rA5As(3NWMfRRtyTgva7glEA^IYZ>#VdlzL08pXWs{rRY4QJ!?_; z&5C@b)~5-PZ&2jBv0m-ARnjkC7P{j13Zs`r;rE>QOR4#iM81J#{2(m+J`lQ6^(Ks8 zE#uFA;rFiaD>Z-AN_)eizf%1&Ve~BgstbBK9%OtOT!zlrZBpc05IvW&+ko&pBke6k z7Z$y|EPhpTycfFXgszm`nxtNz%!9@LY}zp=bgt;Hl%89p-uFd+rQ|DC>xa+FXaEa} zkD@d4ogjQ;qV|nCj_*nIDgvLK#y23U#=?^0KHxB%cZt)1+i{;|E$*J=-)rGJFs=81 z7U_)7BXlCpi0@4lMV#@4TAXo5JpZl<-(}gdn<$NR#s-A%BhJ`~a2;{RFCi=;o`1)L z@1ETA5bk2&o(Q9h(24i}F#a&kRUpoP9N@G2k%zb+_*gS!;5g9SF=2fFAmlmKKe*jJX9S^?i@!fxpI+4!!9>OH^0Y3Cqv<2}d;AhX^ zDjnka_c8b`hqdbReA(`%M&P z8eks+`waG;=$ia1HUKf`S(Ql{>YXQ^pii)Sc`ZXaeU9GZUmMg|Gvn; zD0~lL8Z?Z5j4+3|bq@d8#29ReIKJIe{kV?j-w_EQ&D;TDoXMcvyvFwte-LrTjpyOp zh+Dv~Aovl_zrT@xw}bC^tiFVHf`;*<2os1iK7#N%;`w(r_?}1Q6^s=~XWWRefH>o? zAe8+n>IFVMi9U(=B=AjyTEzL@s`WcspNxB7!5D({M&RoR3B(!S`v%cf#210PUe$fU z2Mi&wJ{kA^F51dvf#(rEKzst&`AzhxcQpPw;%vJN@b@Kt75KLj$8k|=Kwuii!xBFN zbS2LCg2cZBd>4Uj#CYeFF3aeXIAcEo%lRB|)$i%J7nnw zKrP7Q`?+5!ETojO?t7ye!z7{pQ!?Zn?Bdmx3A%sqkBF8$Xa;@$49V7%vnK&q$if?qN9&RGz|JO;=`d)WtbaHfRbb54VbZ)fvobMdXVoZc$X43w% zGh_2(3uB4#!SSK-Gvn#;490lg=?Z%=2B*)YGwH-|-|*z{)bRB1-0=MH!Z3|^N2*3@ zM|>lVBdsHyBmR-Tk%5u)NM>YmWM*V`WNu_}WND;k)EaFXZ5a)ZCPs%wz6M;AsH zN4@8&&ecGQ#&b>QI?wsfh0l4%tg)7{#8_r*a%@h?L*oOGNXs)lJ~KW)zA(NtPMMmF zm1)YfWc-m%x4xd-t$%Gedim`cb@m3A2>gFK68HZ{OtL;^GoOHf_0(x zLd%8L3yBMT7tUNrUzolyb7A4a;)SY)Fn;eP;*Grq5>1&YYb+yLfi# zY|XGW+%()W93D;#4-KDD_F?O=ZP+qwmw}N%Wfiu`(g?vKwXjGlEYb&yq?JdoMc5u} z4YtMLxuJ8D=cdlhotr;LW8SgaG2dA0Sm#*Z*uYpC7MdBG9a|h*8mk$%#+$}l#>3+Y jW)iD|4}bqBH1IzG&=?GW literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/json.py b/libs/win/pydantic/json.py new file mode 100644 index 00000000..b358b850 --- /dev/null +++ b/libs/win/pydantic/json.py @@ -0,0 +1,112 @@ +import datetime +from collections import deque +from decimal import Decimal +from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from re import Pattern +from types import GeneratorType +from typing import Any, Callable, Dict, Type, Union +from uuid import UUID + +from .color import Color +from .networks import NameEmail +from .types import SecretBytes, SecretStr + +__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat' + + +def isoformat(o: Union[datetime.date, datetime.time]) -> str: + return o.isoformat() + + +def decimal_encoder(dec_value: Decimal) -> Union[int, float]: + """ + Encodes a Decimal as int of there's no exponent, otherwise float + + This is useful when we use ConstrainedDecimal to represent Numeric(x,0) + where a integer (but not int typed) is used. Encoding this as a float + results in failed round-tripping between encode and parse. + Our Id type is a prime example of this. + + >>> decimal_encoder(Decimal("1.0")) + 1.0 + + >>> decimal_encoder(Decimal("1")) + 1 + """ + if dec_value.as_tuple().exponent >= 0: + return int(dec_value) + else: + return float(dec_value) + + +ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { + bytes: lambda o: o.decode(), + Color: str, + datetime.date: isoformat, + datetime.datetime: isoformat, + datetime.time: isoformat, + datetime.timedelta: lambda td: td.total_seconds(), + Decimal: decimal_encoder, + Enum: lambda o: o.value, + frozenset: list, + deque: list, + GeneratorType: list, + IPv4Address: str, + IPv4Interface: str, + IPv4Network: str, + IPv6Address: str, + IPv6Interface: str, + IPv6Network: str, + NameEmail: str, + Path: str, + Pattern: lambda o: o.pattern, + SecretBytes: str, + SecretStr: str, + set: list, + UUID: str, +} + + +def pydantic_encoder(obj: Any) -> Any: + from dataclasses import asdict, is_dataclass + + from .main import BaseModel + + if isinstance(obj, BaseModel): + return obj.dict() + elif is_dataclass(obj): + return asdict(obj) + + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = ENCODERS_BY_TYPE[base] + except KeyError: + continue + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable") + + +def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any: + # Check the class type and its superclasses for a matching encoder + for base in obj.__class__.__mro__[:-1]: + try: + encoder = type_encoders[base] + except KeyError: + continue + + return encoder(obj) + else: # We have exited the for loop without finding a suitable encoder + return pydantic_encoder(obj) + + +def timedelta_isoformat(td: datetime.timedelta) -> str: + """ + ISO 8601 encoding for Python timedelta object. + """ + minutes, seconds = divmod(td.seconds, 60) + hours, minutes = divmod(minutes, 60) + return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S' diff --git a/libs/win/pydantic/main.cp37-win_amd64.pyd b/libs/win/pydantic/main.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..a100804c639f1ef2a317f5723937a31edc5b9010 GIT binary patch literal 385024 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�Vmff(jxlP-GT}jOq$R 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 Z2ahZ1W*d}*j8B4s)8fk+&HvZ`{y(*7BxC>p literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/main.py b/libs/win/pydantic/main.py new file mode 100644 index 00000000..69f3b751 --- /dev/null +++ b/libs/win/pydantic/main.py @@ -0,0 +1,1109 @@ +import warnings +from abc import ABCMeta +from copy import deepcopy +from enum import Enum +from functools import partial +from pathlib import Path +from types import FunctionType, prepare_class, resolve_bases +from typing import ( + TYPE_CHECKING, + AbstractSet, + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, + no_type_check, + overload, +) + +from typing_extensions import dataclass_transform + +from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators +from .config import BaseConfig, Extra, inherit_config, prepare_config +from .error_wrappers import ErrorWrapper, ValidationError +from .errors import ConfigError, DictError, ExtraError, MissingError +from .fields import ( + MAPPING_LIKE_SHAPES, + Field, + FieldInfo, + ModelField, + ModelPrivateAttr, + PrivateAttr, + Undefined, + is_finalvar_with_default_val, +) +from .json import custom_pydantic_encoder, pydantic_encoder +from .parse import Protocol, load_file, load_str_bytes +from .schema import default_ref_template, model_schema +from .types import PyObject, StrBytes +from .typing import ( + AnyCallable, + get_args, + get_origin, + is_classvar, + is_namedtuple, + is_union, + resolve_annotations, + update_model_forward_refs, +) +from .utils import ( + DUNDER_ATTRIBUTES, + ROOT_KEY, + ClassAttribute, + GetterDict, + Representation, + ValueItems, + generate_model_signature, + is_valid_field, + is_valid_private_name, + lenient_issubclass, + sequence_like, + smart_deepcopy, + unique_list, + validate_field_name, +) + +if TYPE_CHECKING: + from inspect import Signature + + from .class_validators import ValidatorListDict + from .types import ModelOrDc + from .typing import ( + AbstractSetIntStr, + AnyClassMethod, + CallableGenerator, + DictAny, + DictStrAny, + MappingIntStrAny, + ReprArgs, + SetStr, + TupleGenerator, + ) + + Model = TypeVar('Model', bound='BaseModel') + +__all__ = 'BaseModel', 'create_model', 'validate_model' + +_T = TypeVar('_T') + + +def validate_custom_root_type(fields: Dict[str, ModelField]) -> None: + if len(fields) > 1: + raise ValueError(f'{ROOT_KEY} cannot be mixed with other fields') + + +def generate_hash_function(frozen: bool) -> Optional[Callable[[Any], int]]: + def hash_function(self_: Any) -> int: + return hash(self_.__class__) + hash(tuple(self_.__dict__.values())) + + return hash_function if frozen else None + + +# If a field is of type `Callable`, its default value should be a function and cannot to ignored. +ANNOTATED_FIELD_UNTOUCHED_TYPES: Tuple[Any, ...] = (property, type, classmethod, staticmethod) +# When creating a `BaseModel` instance, we bypass all the methods, properties... added to the model +UNTOUCHED_TYPES: Tuple[Any, ...] = (FunctionType,) + ANNOTATED_FIELD_UNTOUCHED_TYPES +# Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we need to add this extra +# (somewhat hacky) boolean to keep track of whether we've created the `BaseModel` class yet, and therefore whether it's +# safe to refer to it. If it *hasn't* been created, we assume that the `__new__` call we're in the middle of is for +# the `BaseModel` class, since that's defined immediately after the metaclass. +_is_base_model_class_defined = False + + +@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo)) +class ModelMetaclass(ABCMeta): + @no_type_check # noqa C901 + def __new__(mcs, name, bases, namespace, **kwargs): # noqa C901 + fields: Dict[str, ModelField] = {} + config = BaseConfig + validators: 'ValidatorListDict' = {} + + pre_root_validators, post_root_validators = [], [] + private_attributes: Dict[str, ModelPrivateAttr] = {} + base_private_attributes: Dict[str, ModelPrivateAttr] = {} + slots: SetStr = namespace.get('__slots__', ()) + slots = {slots} if isinstance(slots, str) else set(slots) + class_vars: SetStr = set() + hash_func: Optional[Callable[[Any], int]] = None + + for base in reversed(bases): + if _is_base_model_class_defined and issubclass(base, BaseModel) and base != BaseModel: + fields.update(smart_deepcopy(base.__fields__)) + config = inherit_config(base.__config__, config) + validators = inherit_validators(base.__validators__, validators) + pre_root_validators += base.__pre_root_validators__ + post_root_validators += base.__post_root_validators__ + base_private_attributes.update(base.__private_attributes__) + class_vars.update(base.__class_vars__) + hash_func = base.__hash__ + + resolve_forward_refs = kwargs.pop('__resolve_forward_refs__', True) + allowed_config_kwargs: SetStr = { + key + for key in dir(config) + if not (key.startswith('__') and key.endswith('__')) # skip dunder methods and attributes + } + config_kwargs = {key: kwargs.pop(key) for key in kwargs.keys() & allowed_config_kwargs} + config_from_namespace = namespace.get('Config') + if config_kwargs and config_from_namespace: + raise TypeError('Specifying config in two places is ambiguous, use either Config attribute or class kwargs') + config = inherit_config(config_from_namespace, config, **config_kwargs) + + validators = inherit_validators(extract_validators(namespace), validators) + vg = ValidatorGroup(validators) + + for f in fields.values(): + f.set_config(config) + extra_validators = vg.get_validators(f.name) + if extra_validators: + f.class_validators.update(extra_validators) + # re-run prepare to add extra validators + f.populate_validators() + + prepare_config(config, name) + + untouched_types = ANNOTATED_FIELD_UNTOUCHED_TYPES + + def is_untouched(v: Any) -> bool: + return isinstance(v, untouched_types) or v.__class__.__name__ == 'cython_function_or_method' + + if (namespace.get('__module__'), namespace.get('__qualname__')) != ('pydantic.main', 'BaseModel'): + annotations = resolve_annotations(namespace.get('__annotations__', {}), namespace.get('__module__', None)) + # annotation only fields need to come first in fields + for ann_name, ann_type in annotations.items(): + if is_classvar(ann_type): + class_vars.add(ann_name) + elif is_finalvar_with_default_val(ann_type, namespace.get(ann_name, Undefined)): + class_vars.add(ann_name) + elif is_valid_field(ann_name): + validate_field_name(bases, ann_name) + value = namespace.get(ann_name, Undefined) + allowed_types = get_args(ann_type) if is_union(get_origin(ann_type)) else (ann_type,) + if ( + is_untouched(value) + and ann_type != PyObject + and not any( + lenient_issubclass(get_origin(allowed_type), Type) for allowed_type in allowed_types + ) + ): + continue + fields[ann_name] = ModelField.infer( + name=ann_name, + value=value, + annotation=ann_type, + class_validators=vg.get_validators(ann_name), + config=config, + ) + elif ann_name not in namespace and config.underscore_attrs_are_private: + private_attributes[ann_name] = PrivateAttr() + + untouched_types = UNTOUCHED_TYPES + config.keep_untouched + for var_name, value in namespace.items(): + can_be_changed = var_name not in class_vars and not is_untouched(value) + if isinstance(value, ModelPrivateAttr): + if not is_valid_private_name(var_name): + raise NameError( + f'Private attributes "{var_name}" must not be a valid field name; ' + f'Use sunder or dunder names, e. g. "_{var_name}" or "__{var_name}__"' + ) + private_attributes[var_name] = value + elif config.underscore_attrs_are_private and is_valid_private_name(var_name) and can_be_changed: + private_attributes[var_name] = PrivateAttr(default=value) + elif is_valid_field(var_name) and var_name not in annotations and can_be_changed: + validate_field_name(bases, var_name) + inferred = ModelField.infer( + name=var_name, + value=value, + annotation=annotations.get(var_name, Undefined), + class_validators=vg.get_validators(var_name), + config=config, + ) + if var_name in fields: + if lenient_issubclass(inferred.type_, fields[var_name].type_): + inferred.type_ = fields[var_name].type_ + else: + raise TypeError( + f'The type of {name}.{var_name} differs from the new default value; ' + f'if you wish to change the type of this field, please use a type annotation' + ) + fields[var_name] = inferred + + _custom_root_type = ROOT_KEY in fields + if _custom_root_type: + validate_custom_root_type(fields) + vg.check_for_unused() + if config.json_encoders: + json_encoder = partial(custom_pydantic_encoder, config.json_encoders) + else: + json_encoder = pydantic_encoder + pre_rv_new, post_rv_new = extract_root_validators(namespace) + + if hash_func is None: + hash_func = generate_hash_function(config.frozen) + + exclude_from_namespace = fields | private_attributes.keys() | {'__slots__'} + new_namespace = { + '__config__': config, + '__fields__': fields, + '__exclude_fields__': { + name: field.field_info.exclude for name, field in fields.items() if field.field_info.exclude is not None + } + or None, + '__include_fields__': { + name: field.field_info.include for name, field in fields.items() if field.field_info.include is not None + } + or None, + '__validators__': vg.validators, + '__pre_root_validators__': unique_list( + pre_root_validators + pre_rv_new, + name_factory=lambda v: v.__name__, + ), + '__post_root_validators__': unique_list( + post_root_validators + post_rv_new, + name_factory=lambda skip_on_failure_and_v: skip_on_failure_and_v[1].__name__, + ), + '__schema_cache__': {}, + '__json_encoder__': staticmethod(json_encoder), + '__custom_root_type__': _custom_root_type, + '__private_attributes__': {**base_private_attributes, **private_attributes}, + '__slots__': slots | private_attributes.keys(), + '__hash__': hash_func, + '__class_vars__': class_vars, + **{n: v for n, v in namespace.items() if n not in exclude_from_namespace}, + } + + cls = super().__new__(mcs, name, bases, new_namespace, **kwargs) + # set __signature__ attr only for model class, but not for its instances + cls.__signature__ = ClassAttribute('__signature__', generate_model_signature(cls.__init__, fields, config)) + if resolve_forward_refs: + cls.__try_update_forward_refs__() + + # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487 + # for attributes not in `new_namespace` (e.g. private attributes) + for name, obj in namespace.items(): + if name not in new_namespace: + set_name = getattr(obj, '__set_name__', None) + if callable(set_name): + set_name(cls, name) + + return cls + + def __instancecheck__(self, instance: Any) -> bool: + """ + Avoid calling ABC _abc_subclasscheck unless we're pretty sure. + + See #3829 and python/cpython#92810 + """ + return hasattr(instance, '__fields__') and super().__instancecheck__(instance) + + +object_setattr = object.__setattr__ + + +class BaseModel(Representation, metaclass=ModelMetaclass): + if TYPE_CHECKING: + # populated by the metaclass, defined here to help IDEs only + __fields__: ClassVar[Dict[str, ModelField]] = {} + __include_fields__: ClassVar[Optional[Mapping[str, Any]]] = None + __exclude_fields__: ClassVar[Optional[Mapping[str, Any]]] = None + __validators__: ClassVar[Dict[str, AnyCallable]] = {} + __pre_root_validators__: ClassVar[List[AnyCallable]] + __post_root_validators__: ClassVar[List[Tuple[bool, AnyCallable]]] + __config__: ClassVar[Type[BaseConfig]] = BaseConfig + __json_encoder__: ClassVar[Callable[[Any], Any]] = lambda x: x + __schema_cache__: ClassVar['DictAny'] = {} + __custom_root_type__: ClassVar[bool] = False + __signature__: ClassVar['Signature'] + __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]] + __class_vars__: ClassVar[SetStr] + __fields_set__: ClassVar[SetStr] = set() + + Config = BaseConfig + __slots__ = ('__dict__', '__fields_set__') + __doc__ = '' # Null out the Representation docstring + + def __init__(__pydantic_self__, **data: Any) -> None: + """ + Create a new model by parsing and validating input data from keyword arguments. + + Raises ValidationError if the input data cannot be parsed to form a valid model. + """ + # Uses something other than `self` the first arg to allow "self" as a settable attribute + values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data) + if validation_error: + raise validation_error + try: + object_setattr(__pydantic_self__, '__dict__', values) + except TypeError as e: + raise TypeError( + 'Model values must be a dict; you may not have returned a dictionary from a root validator' + ) from e + object_setattr(__pydantic_self__, '__fields_set__', fields_set) + __pydantic_self__._init_private_attributes() + + @no_type_check + def __setattr__(self, name, value): # noqa: C901 (ignore complexity) + if name in self.__private_attributes__ or name in DUNDER_ATTRIBUTES: + return object_setattr(self, name, value) + + if self.__config__.extra is not Extra.allow and name not in self.__fields__: + raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"') + elif not self.__config__.allow_mutation or self.__config__.frozen: + raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment') + elif name in self.__fields__ and self.__fields__[name].final: + raise TypeError( + f'"{self.__class__.__name__}" object "{name}" field is final and does not support reassignment' + ) + elif self.__config__.validate_assignment: + new_values = {**self.__dict__, name: value} + + for validator in self.__pre_root_validators__: + try: + new_values = validator(self.__class__, new_values) + except (ValueError, TypeError, AssertionError) as exc: + raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], self.__class__) + + known_field = self.__fields__.get(name, None) + if known_field: + # We want to + # - make sure validators are called without the current value for this field inside `values` + # - keep other values (e.g. submodels) untouched (using `BaseModel.dict()` will change them into dicts) + # - keep the order of the fields + if not known_field.field_info.allow_mutation: + raise TypeError(f'"{known_field.name}" has allow_mutation set to False and cannot be assigned') + dict_without_original_value = {k: v for k, v in self.__dict__.items() if k != name} + value, error_ = known_field.validate(value, dict_without_original_value, loc=name, cls=self.__class__) + if error_: + raise ValidationError([error_], self.__class__) + else: + new_values[name] = value + + errors = [] + for skip_on_failure, validator in self.__post_root_validators__: + if skip_on_failure and errors: + continue + try: + new_values = validator(self.__class__, new_values) + except (ValueError, TypeError, AssertionError) as exc: + errors.append(ErrorWrapper(exc, loc=ROOT_KEY)) + if errors: + raise ValidationError(errors, self.__class__) + + # update the whole __dict__ as other values than just `value` + # may be changed (e.g. with `root_validator`) + object_setattr(self, '__dict__', new_values) + else: + self.__dict__[name] = value + + self.__fields_set__.add(name) + + def __getstate__(self) -> 'DictAny': + private_attrs = ((k, getattr(self, k, Undefined)) for k in self.__private_attributes__) + return { + '__dict__': self.__dict__, + '__fields_set__': self.__fields_set__, + '__private_attribute_values__': {k: v for k, v in private_attrs if v is not Undefined}, + } + + def __setstate__(self, state: 'DictAny') -> None: + object_setattr(self, '__dict__', state['__dict__']) + object_setattr(self, '__fields_set__', state['__fields_set__']) + for name, value in state.get('__private_attribute_values__', {}).items(): + object_setattr(self, name, value) + + def _init_private_attributes(self) -> None: + for name, private_attr in self.__private_attributes__.items(): + default = private_attr.get_default() + if default is not Undefined: + object_setattr(self, name, default) + + def dict( + self, + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + by_alias: bool = False, + skip_defaults: Optional[bool] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> 'DictStrAny': + """ + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + """ + if skip_defaults is not None: + warnings.warn( + f'{self.__class__.__name__}.dict(): "skip_defaults" is deprecated and replaced by "exclude_unset"', + DeprecationWarning, + ) + exclude_unset = skip_defaults + + return dict( + self._iter( + to_dict=True, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + ) + + def json( + self, + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + by_alias: bool = False, + skip_defaults: Optional[bool] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + encoder: Optional[Callable[[Any], Any]] = None, + models_as_dict: bool = True, + **dumps_kwargs: Any, + ) -> str: + """ + Generate a JSON representation of the model, `include` and `exclude` arguments as per `dict()`. + + `encoder` is an optional function to supply as `default` to json.dumps(), other arguments as per `json.dumps()`. + """ + if skip_defaults is not None: + warnings.warn( + f'{self.__class__.__name__}.json(): "skip_defaults" is deprecated and replaced by "exclude_unset"', + DeprecationWarning, + ) + exclude_unset = skip_defaults + encoder = cast(Callable[[Any], Any], encoder or self.__json_encoder__) + + # We don't directly call `self.dict()`, which does exactly this with `to_dict=True` + # because we want to be able to keep raw `BaseModel` instances and not as `dict`. + # This allows users to write custom JSON encoders for given `BaseModel` classes. + data = dict( + self._iter( + to_dict=models_as_dict, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + ) + if self.__custom_root_type__: + data = data[ROOT_KEY] + return self.__config__.json_dumps(data, default=encoder, **dumps_kwargs) + + @classmethod + def _enforce_dict_if_root(cls, obj: Any) -> Any: + if cls.__custom_root_type__ and ( + not (isinstance(obj, dict) and obj.keys() == {ROOT_KEY}) + or cls.__fields__[ROOT_KEY].shape in MAPPING_LIKE_SHAPES + ): + return {ROOT_KEY: obj} + else: + return obj + + @classmethod + def parse_obj(cls: Type['Model'], obj: Any) -> 'Model': + obj = cls._enforce_dict_if_root(obj) + if not isinstance(obj, dict): + try: + obj = dict(obj) + except (TypeError, ValueError) as e: + exc = TypeError(f'{cls.__name__} expected dict not {obj.__class__.__name__}') + raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls) from e + return cls(**obj) + + @classmethod + def parse_raw( + cls: Type['Model'], + b: StrBytes, + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + ) -> 'Model': + try: + obj = load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=cls.__config__.json_loads, + ) + except (ValueError, TypeError, UnicodeDecodeError) as e: + raise ValidationError([ErrorWrapper(e, loc=ROOT_KEY)], cls) + return cls.parse_obj(obj) + + @classmethod + def parse_file( + cls: Type['Model'], + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + ) -> 'Model': + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=cls.__config__.json_loads, + ) + return cls.parse_obj(obj) + + @classmethod + def from_orm(cls: Type['Model'], obj: Any) -> 'Model': + if not cls.__config__.orm_mode: + raise ConfigError('You must have the config attribute orm_mode=True to use from_orm') + obj = {ROOT_KEY: obj} if cls.__custom_root_type__ else cls._decompose_class(obj) + m = cls.__new__(cls) + values, fields_set, validation_error = validate_model(cls, obj) + if validation_error: + raise validation_error + object_setattr(m, '__dict__', values) + object_setattr(m, '__fields_set__', fields_set) + m._init_private_attributes() + return m + + @classmethod + def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **values: Any) -> 'Model': + """ + Creates a new model setting __dict__ and __fields_set__ from trusted or pre-validated data. + Default values are respected, but no other validation is performed. + Behaves as if `Config.extra = 'allow'` was set since it adds all passed values + """ + m = cls.__new__(cls) + fields_values: Dict[str, Any] = {} + for name, field in cls.__fields__.items(): + if field.alt_alias and field.alias in values: + fields_values[name] = values[field.alias] + elif name in values: + fields_values[name] = values[name] + elif not field.required: + fields_values[name] = field.get_default() + fields_values.update(values) + object_setattr(m, '__dict__', fields_values) + if _fields_set is None: + _fields_set = set(values.keys()) + object_setattr(m, '__fields_set__', _fields_set) + m._init_private_attributes() + return m + + def _copy_and_set_values(self: 'Model', values: 'DictStrAny', fields_set: 'SetStr', *, deep: bool) -> 'Model': + if deep: + # chances of having empty dict here are quite low for using smart_deepcopy + values = deepcopy(values) + + cls = self.__class__ + m = cls.__new__(cls) + object_setattr(m, '__dict__', values) + object_setattr(m, '__fields_set__', fields_set) + for name in self.__private_attributes__: + value = getattr(self, name, Undefined) + if value is not Undefined: + if deep: + value = deepcopy(value) + object_setattr(m, name, value) + + return m + + def copy( + self: 'Model', + *, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + update: Optional['DictStrAny'] = None, + deep: bool = False, + ) -> 'Model': + """ + Duplicate a model, optionally choose which fields to include, exclude and change. + + :param include: fields to include in new model + :param exclude: fields to exclude from new model, as with values this takes precedence over include + :param update: values to change/add in the new model. Note: the data is not validated before creating + the new model: you should trust this data + :param deep: set to `True` to make a deep copy of the model + :return: new model instance + """ + + values = dict( + self._iter(to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False), + **(update or {}), + ) + + # new `__fields_set__` can have unset optional fields with a set value in `update` kwarg + if update: + fields_set = self.__fields_set__ | update.keys() + else: + fields_set = set(self.__fields_set__) + + return self._copy_and_set_values(values, fields_set, deep=deep) + + @classmethod + def schema(cls, by_alias: bool = True, ref_template: str = default_ref_template) -> 'DictStrAny': + cached = cls.__schema_cache__.get((by_alias, ref_template)) + if cached is not None: + return cached + s = model_schema(cls, by_alias=by_alias, ref_template=ref_template) + cls.__schema_cache__[(by_alias, ref_template)] = s + return s + + @classmethod + def schema_json( + cls, *, by_alias: bool = True, ref_template: str = default_ref_template, **dumps_kwargs: Any + ) -> str: + from .json import pydantic_encoder + + return cls.__config__.json_dumps( + cls.schema(by_alias=by_alias, ref_template=ref_template), default=pydantic_encoder, **dumps_kwargs + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls: Type['Model'], value: Any) -> 'Model': + if isinstance(value, cls): + copy_on_model_validation = cls.__config__.copy_on_model_validation + # whether to deep or shallow copy the model on validation, None means do not copy + deep_copy: Optional[bool] = None + if copy_on_model_validation not in {'deep', 'shallow', 'none'}: + # Warn about deprecated behavior + warnings.warn( + "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", DeprecationWarning + ) + if copy_on_model_validation: + deep_copy = False + + if copy_on_model_validation == 'shallow': + # shallow copy + deep_copy = False + elif copy_on_model_validation == 'deep': + # deep copy + deep_copy = True + + if deep_copy is None: + return value + else: + return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=deep_copy) + + value = cls._enforce_dict_if_root(value) + + if isinstance(value, dict): + return cls(**value) + elif cls.__config__.orm_mode: + return cls.from_orm(value) + else: + try: + value_as_dict = dict(value) + except (TypeError, ValueError) as e: + raise DictError() from e + return cls(**value_as_dict) + + @classmethod + def _decompose_class(cls: Type['Model'], obj: Any) -> GetterDict: + if isinstance(obj, GetterDict): + return obj + return cls.__config__.getter_dict(obj) + + @classmethod + @no_type_check + def _get_value( + cls, + v: Any, + to_dict: bool, + by_alias: bool, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']], + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']], + exclude_unset: bool, + exclude_defaults: bool, + exclude_none: bool, + ) -> Any: + + if isinstance(v, BaseModel): + if to_dict: + v_dict = v.dict( + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=include, + exclude=exclude, + exclude_none=exclude_none, + ) + if ROOT_KEY in v_dict: + return v_dict[ROOT_KEY] + return v_dict + else: + return v.copy(include=include, exclude=exclude) + + value_exclude = ValueItems(v, exclude) if exclude else None + value_include = ValueItems(v, include) if include else None + + if isinstance(v, dict): + return { + k_: cls._get_value( + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(k_), + exclude=value_exclude and value_exclude.for_element(k_), + exclude_none=exclude_none, + ) + for k_, v_ in v.items() + if (not value_exclude or not value_exclude.is_excluded(k_)) + and (not value_include or value_include.is_included(k_)) + } + + elif sequence_like(v): + seq_args = ( + cls._get_value( + v_, + to_dict=to_dict, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + include=value_include and value_include.for_element(i), + exclude=value_exclude and value_exclude.for_element(i), + exclude_none=exclude_none, + ) + for i, v_ in enumerate(v) + if (not value_exclude or not value_exclude.is_excluded(i)) + and (not value_include or value_include.is_included(i)) + ) + + return v.__class__(*seq_args) if is_namedtuple(v.__class__) else v.__class__(seq_args) + + elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False): + return v.value + + else: + return v + + @classmethod + def __try_update_forward_refs__(cls, **localns: Any) -> None: + """ + Same as update_forward_refs but will not raise exception + when forward references are not defined. + """ + update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,)) + + @classmethod + def update_forward_refs(cls, **localns: Any) -> None: + """ + Try to update ForwardRefs on fields based on this Model, globalns and localns. + """ + update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns) + + def __iter__(self) -> 'TupleGenerator': + """ + so `dict(model)` works + """ + yield from self.__dict__.items() + + def _iter( + self, + to_dict: bool = False, + by_alias: bool = False, + include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> 'TupleGenerator': + + # Merge field set excludes with explicit exclude parameter with explicit overriding field set options. + # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case. + if exclude is not None or self.__exclude_fields__ is not None: + exclude = ValueItems.merge(self.__exclude_fields__, exclude) + + if include is not None or self.__include_fields__ is not None: + include = ValueItems.merge(self.__include_fields__, include, intersect=True) + + allowed_keys = self._calculate_keys( + include=include, exclude=exclude, exclude_unset=exclude_unset # type: ignore + ) + if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none): + # huge boost for plain _iter() + yield from self.__dict__.items() + return + + value_exclude = ValueItems(self, exclude) if exclude is not None else None + value_include = ValueItems(self, include) if include is not None else None + + for field_key, v in self.__dict__.items(): + if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None): + continue + + if exclude_defaults: + model_field = self.__fields__.get(field_key) + if not getattr(model_field, 'required', True) and getattr(model_field, 'default', _missing) == v: + continue + + if by_alias and field_key in self.__fields__: + dict_key = self.__fields__[field_key].alias + else: + dict_key = field_key + + if to_dict or value_include or value_exclude: + v = self._get_value( + v, + to_dict=to_dict, + by_alias=by_alias, + include=value_include and value_include.for_element(field_key), + exclude=value_exclude and value_exclude.for_element(field_key), + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + yield dict_key, v + + def _calculate_keys( + self, + include: Optional['MappingIntStrAny'], + exclude: Optional['MappingIntStrAny'], + exclude_unset: bool, + update: Optional['DictStrAny'] = None, + ) -> Optional[AbstractSet[str]]: + if include is None and exclude is None and exclude_unset is False: + return None + + keys: AbstractSet[str] + if exclude_unset: + keys = self.__fields_set__.copy() + else: + keys = self.__dict__.keys() + + if include is not None: + keys &= include.keys() + + if update: + keys -= update.keys() + + if exclude: + keys -= {k for k, v in exclude.items() if ValueItems.is_true(v)} + + return keys + + def __eq__(self, other: Any) -> bool: + if isinstance(other, BaseModel): + return self.dict() == other.dict() + else: + return self.dict() == other + + def __repr_args__(self) -> 'ReprArgs': + return [ + (k, v) + for k, v in self.__dict__.items() + if k not in DUNDER_ATTRIBUTES and (k not in self.__fields__ or self.__fields__[k].field_info.repr) + ] + + +_is_base_model_class_defined = True + + +@overload +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: None = None, + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + **field_definitions: Any, +) -> Type['BaseModel']: + ... + + +@overload +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: Union[Type['Model'], Tuple[Type['Model'], ...]], + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + **field_definitions: Any, +) -> Type['Model']: + ... + + +def create_model( + __model_name: str, + *, + __config__: Optional[Type[BaseConfig]] = None, + __base__: Union[None, Type['Model'], Tuple[Type['Model'], ...]] = None, + __module__: str = __name__, + __validators__: Dict[str, 'AnyClassMethod'] = None, + __cls_kwargs__: Dict[str, Any] = None, + __slots__: Optional[Tuple[str, ...]] = None, + **field_definitions: Any, +) -> Type['Model']: + """ + Dynamically create a model. + :param __model_name: name of the created model + :param __config__: config class to use for the new model + :param __base__: base class for the new model to inherit from + :param __module__: module of the created model + :param __validators__: a dict of method names and @validator class methods + :param __cls_kwargs__: a dict for class creation + :param __slots__: Deprecated, `__slots__` should not be passed to `create_model` + :param field_definitions: fields of the model (or extra fields if a base is supplied) + in the format `=(, )` or `=, e.g. + `foobar=(str, ...)` or `foobar=123`, or, for complex use-cases, in the format + `=` or `=(, )`, e.g. + `foo=Field(datetime, default_factory=datetime.utcnow, alias='bar')` or + `foo=(str, FieldInfo(title='Foo'))` + """ + if __slots__ is not None: + # __slots__ will be ignored from here on + warnings.warn('__slots__ should not be passed to create_model', RuntimeWarning) + + if __base__ is not None: + if __config__ is not None: + raise ConfigError('to avoid confusion __config__ and __base__ cannot be used together') + if not isinstance(__base__, tuple): + __base__ = (__base__,) + else: + __base__ = (cast(Type['Model'], BaseModel),) + + __cls_kwargs__ = __cls_kwargs__ or {} + + fields = {} + annotations = {} + + for f_name, f_def in field_definitions.items(): + if not is_valid_field(f_name): + warnings.warn(f'fields may not start with an underscore, ignoring "{f_name}"', RuntimeWarning) + if isinstance(f_def, tuple): + try: + f_annotation, f_value = f_def + except ValueError as e: + raise ConfigError( + 'field definitions should either be a tuple of (, ) or just a ' + 'default value, unfortunately this means tuples as ' + 'default values are not allowed' + ) from e + else: + f_annotation, f_value = None, f_def + + if f_annotation: + annotations[f_name] = f_annotation + fields[f_name] = f_value + + namespace: 'DictStrAny' = {'__annotations__': annotations, '__module__': __module__} + if __validators__: + namespace.update(__validators__) + namespace.update(fields) + if __config__: + namespace['Config'] = inherit_config(__config__, BaseConfig) + resolved_bases = resolve_bases(__base__) + meta, ns, kwds = prepare_class(__model_name, resolved_bases, kwds=__cls_kwargs__) + if resolved_bases is not __base__: + ns['__orig_bases__'] = __base__ + namespace.update(ns) + return meta(__model_name, resolved_bases, namespace, **kwds) + + +_missing = object() + + +def validate_model( # noqa: C901 (ignore complexity) + model: Type[BaseModel], input_data: 'DictStrAny', cls: 'ModelOrDc' = None +) -> Tuple['DictStrAny', 'SetStr', Optional[ValidationError]]: + """ + validate data against a model. + """ + values = {} + errors = [] + # input_data names, possibly alias + names_used = set() + # field names, never aliases + fields_set = set() + config = model.__config__ + check_extra = config.extra is not Extra.ignore + cls_ = cls or model + + for validator in model.__pre_root_validators__: + try: + input_data = validator(cls_, input_data) + except (ValueError, TypeError, AssertionError) as exc: + return {}, set(), ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls_) + + for name, field in model.__fields__.items(): + value = input_data.get(field.alias, _missing) + using_name = False + if value is _missing and config.allow_population_by_field_name and field.alt_alias: + value = input_data.get(field.name, _missing) + using_name = True + + if value is _missing: + if field.required: + errors.append(ErrorWrapper(MissingError(), loc=field.alias)) + continue + + value = field.get_default() + + if not config.validate_all and not field.validate_always: + values[name] = value + continue + else: + fields_set.add(name) + if check_extra: + names_used.add(field.name if using_name else field.alias) + + v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls_) + if isinstance(errors_, ErrorWrapper): + errors.append(errors_) + elif isinstance(errors_, list): + errors.extend(errors_) + else: + values[name] = v_ + + if check_extra: + if isinstance(input_data, GetterDict): + extra = input_data.extra_keys() - names_used + else: + extra = input_data.keys() - names_used + if extra: + fields_set |= extra + if config.extra is Extra.allow: + for f in extra: + values[f] = input_data[f] + else: + for f in sorted(extra): + errors.append(ErrorWrapper(ExtraError(), loc=f)) + + for skip_on_failure, validator in model.__post_root_validators__: + if skip_on_failure and errors: + continue + try: + values = validator(cls_, values) + except (ValueError, TypeError, AssertionError) as exc: + errors.append(ErrorWrapper(exc, loc=ROOT_KEY)) + + if errors: + return values, fields_set, ValidationError(errors, cls_) + else: + return values, fields_set, None diff --git a/libs/win/pydantic/mypy.cp37-win_amd64.pyd b/libs/win/pydantic/mypy.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..3611c95bb5abdd7363c9edf5ee8eee0eb341391a GIT binary patch literal 325632 zcmd?ScYM@U_WvKs0G4rL8I6S{YFM#EQAFcL)(j*%qZ5rvQ$bKbKtWJ4iXwuNtjxY0 z+p6o@3zoHakrINSpj0ckxQc6=7z>s~EbIK9uk(IS$z<@ezwhJo`~Cj-@py!JmwV4W z_uO+&yXW3_?djJQbt)<<>dap%Ra7*WU;V4H|9|_x;toYcJM@^hL(#L_tlE2Shv=%k z2aX#*rF8Ozlg3_n*2L0LXPtlkr25jc$CO?eJHK@N`K8q-omx6^(&#Y<`~BONd7yWg ze69cZ9X~uX{WoCzg=dDjf9>3jBlO#0y!-pwxgU=>&YpiVqQCwAYeaOKcb{yk>s&OH zI^mN$7mXIoDqqnS{g(QQk~`|c`&Z{Hs^7M#=*f!CMSG>675JTeWapwP7j^gTrcOmk zP}|zSgX_mkt>a0(Uh>k2@Xx@*|+FNdGP&bqL(O)9wZV_h!^JHyI)hn)#!6=A2otSUTfY3Yb1LWK4z!j-`q zs??7OJN>&w9e;1Ij8rbJA5`1;YV3Ca+)o%Jg3GrE7C^6}bf0E`Q~=n2J;kF5-_xJy zZB+>cU!Y*E6DbMDtIJBQ#vR!j$?JVsrg9#IMh66j+E9y+|9ga^gt!-=$G{#};g^$6rEdwdSqu7pNr|-4f zb}m|2r>B#wVoh<_2^WXy-67!rRm>3_;t&pdDh+42D7Tbx@}gr*;b;)iD+gJJU+QqkcfxnF6&}(2ub5emK=7JZt|B zu`Xe!BbB0#Z_X#3X#`$fo!oJXdUHsJsIy62mxkQB6DUr%Vk?qg)^~LQVG2lFG%NbFK zp73{ueM56kg#^yq$*ca3fN@rZmNm}gfrcYl)+CYAx^E;=TN;itv*XO{`1Ib%N1oO= zE%VB1`bd^lNtT5r%j&|;Wo1Jp&_W|ZXNHEGRLhFks=*Q@2nqi_-+^_aU*+eiRJG$9 z`fu)#JplTL#BVqnA6-^g)92&JtV>EuLe9#NvowlC9vZlDaa2O72Jv!NbK~;*--lAm zBcqzDZrmJb zNOA%5@3sYz;H-3l=Swy?OT+Oqi;?J&SyM|ZXt(hVx5a*`*W$qwC=wp^QrKCrY%=XJ zbEEOGG�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�%VdxUqQmP3i|H)&=JRF7EGWI)+;GspY?RVA8Z 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%0D3g*)(KmaNX)%Vx12SK3eImMJW=DL52`iYGjch$6 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/mypy.py b/libs/win/pydantic/mypy.py new file mode 100644 index 00000000..6bd9db18 --- /dev/null +++ b/libs/win/pydantic/mypy.py @@ -0,0 +1,850 @@ +import sys +from configparser import ConfigParser +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType, Union + +from mypy.errorcodes import ErrorCode +from mypy.nodes import ( + ARG_NAMED, + ARG_NAMED_OPT, + ARG_OPT, + ARG_POS, + ARG_STAR2, + MDEF, + Argument, + AssignmentStmt, + Block, + CallExpr, + ClassDef, + Context, + Decorator, + EllipsisExpr, + FuncBase, + FuncDef, + JsonDict, + MemberExpr, + NameExpr, + PassStmt, + PlaceholderNode, + RefExpr, + StrExpr, + SymbolNode, + SymbolTableNode, + TempNode, + TypeInfo, + TypeVarExpr, + Var, +) +from mypy.options import Options +from mypy.plugin import ( + CheckerPluginInterface, + ClassDefContext, + FunctionContext, + MethodContext, + Plugin, + SemanticAnalyzerPluginInterface, +) +from mypy.plugins import dataclasses +from mypy.semanal import set_callable_name # type: ignore +from mypy.server.trigger import make_wildcard_trigger +from mypy.types import ( + AnyType, + CallableType, + Instance, + NoneType, + Overloaded, + Type, + TypeOfAny, + TypeType, + TypeVarType, + UnionType, + get_proper_type, +) +from mypy.typevars import fill_typevars +from mypy.util import get_unique_redefinition_name +from mypy.version import __version__ as mypy_version + +from pydantic.utils import is_valid_field + +try: + from mypy.types import TypeVarDef # type: ignore[attr-defined] +except ImportError: # pragma: no cover + # Backward-compatible with TypeVarDef from Mypy 0.910. + from mypy.types import TypeVarType as TypeVarDef + +CONFIGFILE_KEY = 'pydantic-mypy' +METADATA_KEY = 'pydantic-mypy-metadata' +BASEMODEL_FULLNAME = 'pydantic.main.BaseModel' +BASESETTINGS_FULLNAME = 'pydantic.env_settings.BaseSettings' +FIELD_FULLNAME = 'pydantic.fields.Field' +DATACLASS_FULLNAME = 'pydantic.dataclasses.dataclass' + + +def parse_mypy_version(version: str) -> Tuple[int, ...]: + return tuple(int(part) for part in version.split('+', 1)[0].split('.')) + + +MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version) +BUILTINS_NAME = 'builtins' if MYPY_VERSION_TUPLE >= (0, 930) else '__builtins__' + + +def plugin(version: str) -> 'TypingType[Plugin]': + """ + `version` is the mypy version string + + We might want to use this to print a warning if the mypy version being used is + newer, or especially older, than we expect (or need). + """ + return PydanticPlugin + + +class PydanticPlugin(Plugin): + def __init__(self, options: Options) -> None: + self.plugin_config = PydanticPluginConfig(options) + super().__init__(options) + + def get_base_class_hook(self, fullname: str) -> 'Optional[Callable[[ClassDefContext], None]]': + sym = self.lookup_fully_qualified(fullname) + if sym and isinstance(sym.node, TypeInfo): # pragma: no branch + # No branching may occur if the mypy cache has not been cleared + if any(get_fullname(base) == BASEMODEL_FULLNAME for base in sym.node.mro): + return self._pydantic_model_class_maker_callback + return None + + def get_function_hook(self, fullname: str) -> 'Optional[Callable[[FunctionContext], Type]]': + sym = self.lookup_fully_qualified(fullname) + if sym and sym.fullname == FIELD_FULLNAME: + return self._pydantic_field_callback + return None + + def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]: + if fullname.endswith('.from_orm'): + return from_orm_callback + return None + + def get_class_decorator_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]: + if fullname == DATACLASS_FULLNAME: + return dataclasses.dataclass_class_maker_callback # type: ignore[return-value] + return None + + def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> None: + transformer = PydanticModelTransformer(ctx, self.plugin_config) + transformer.transform() + + def _pydantic_field_callback(self, ctx: FunctionContext) -> 'Type': + """ + Extract the type of the `default` argument from the Field function, and use it as the return type. + + In particular: + * Check whether the default and default_factory argument is specified. + * Output an error if both are specified. + * Retrieve the type of the argument which is specified, and use it as return type for the function. + """ + default_any_type = ctx.default_return_type + + assert ctx.callee_arg_names[0] == 'default', '"default" is no longer first argument in Field()' + assert ctx.callee_arg_names[1] == 'default_factory', '"default_factory" is no longer second argument in Field()' + default_args = ctx.args[0] + default_factory_args = ctx.args[1] + + if default_args and default_factory_args: + error_default_and_default_factory_specified(ctx.api, ctx.context) + return default_any_type + + if default_args: + default_type = ctx.arg_types[0][0] + default_arg = default_args[0] + + # Fallback to default Any type if the field is required + if not isinstance(default_arg, EllipsisExpr): + return default_type + + elif default_factory_args: + default_factory_type = ctx.arg_types[1][0] + + # Functions which use `ParamSpec` can be overloaded, exposing the callable's types as a parameter + # Pydantic calls the default factory without any argument, so we retrieve the first item + if isinstance(default_factory_type, Overloaded): + if MYPY_VERSION_TUPLE > (0, 910): + default_factory_type = default_factory_type.items[0] + else: + # Mypy0.910 exposes the items of overloaded types in a function + default_factory_type = default_factory_type.items()[0] # type: ignore[operator] + + if isinstance(default_factory_type, CallableType): + ret_type = default_factory_type.ret_type + # mypy doesn't think `ret_type` has `args`, you'd think mypy should know, + # add this check in case it varies by version + args = getattr(ret_type, 'args', None) + if args: + if all(isinstance(arg, TypeVarType) for arg in args): + # Looks like the default factory is a type like `list` or `dict`, replace all args with `Any` + ret_type.args = tuple(default_any_type for _ in args) # type: ignore[attr-defined] + return ret_type + + return default_any_type + + +class PydanticPluginConfig: + __slots__ = ('init_forbid_extra', 'init_typed', 'warn_required_dynamic_aliases', 'warn_untyped_fields') + init_forbid_extra: bool + init_typed: bool + warn_required_dynamic_aliases: bool + warn_untyped_fields: bool + + def __init__(self, options: Options) -> None: + if options.config_file is None: # pragma: no cover + return + + toml_config = parse_toml(options.config_file) + if toml_config is not None: + config = toml_config.get('tool', {}).get('pydantic-mypy', {}) + for key in self.__slots__: + setting = config.get(key, False) + if not isinstance(setting, bool): + raise ValueError(f'Configuration value must be a boolean for key: {key}') + setattr(self, key, setting) + else: + plugin_config = ConfigParser() + plugin_config.read(options.config_file) + for key in self.__slots__: + setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False) + setattr(self, key, setting) + + +def from_orm_callback(ctx: MethodContext) -> Type: + """ + Raise an error if orm_mode is not enabled + """ + model_type: Instance + if isinstance(ctx.type, CallableType) and isinstance(ctx.type.ret_type, Instance): + model_type = ctx.type.ret_type # called on the class + elif isinstance(ctx.type, Instance): + model_type = ctx.type # called on an instance (unusual, but still valid) + else: # pragma: no cover + detail = f'ctx.type: {ctx.type} (of type {ctx.type.__class__.__name__})' + error_unexpected_behavior(detail, ctx.api, ctx.context) + return ctx.default_return_type + pydantic_metadata = model_type.type.metadata.get(METADATA_KEY) + if pydantic_metadata is None: + return ctx.default_return_type + orm_mode = pydantic_metadata.get('config', {}).get('orm_mode') + if orm_mode is not True: + error_from_orm(get_name(model_type.type), ctx.api, ctx.context) + return ctx.default_return_type + + +class PydanticModelTransformer: + tracked_config_fields: Set[str] = { + 'extra', + 'allow_mutation', + 'frozen', + 'orm_mode', + 'allow_population_by_field_name', + 'alias_generator', + } + + def __init__(self, ctx: ClassDefContext, plugin_config: PydanticPluginConfig) -> None: + self._ctx = ctx + self.plugin_config = plugin_config + + def transform(self) -> None: + """ + Configures the BaseModel subclass according to the plugin settings. + + In particular: + * determines the model config and fields, + * adds a fields-aware signature for the initializer and construct methods + * freezes the class if allow_mutation = False or frozen = True + * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses + """ + ctx = self._ctx + info = self._ctx.cls.info + + self.adjust_validator_signatures() + config = self.collect_config() + fields = self.collect_fields(config) + for field in fields: + if info[field.name].type is None: + if not ctx.api.final_iteration: + ctx.api.defer() + is_settings = any(get_fullname(base) == BASESETTINGS_FULLNAME for base in info.mro[:-1]) + self.add_initializer(fields, config, is_settings) + self.add_construct_method(fields) + self.set_frozen(fields, frozen=config.allow_mutation is False or config.frozen is True) + info.metadata[METADATA_KEY] = { + 'fields': {field.name: field.serialize() for field in fields}, + 'config': config.set_values_dict(), + } + + def adjust_validator_signatures(self) -> None: + """When we decorate a function `f` with `pydantic.validator(...), mypy sees + `f` as a regular method taking a `self` instance, even though pydantic + internally wraps `f` with `classmethod` if necessary. + + Teach mypy this by marking any function whose outermost decorator is a + `validator()` call as a classmethod. + """ + for name, sym in self._ctx.cls.info.names.items(): + if isinstance(sym.node, Decorator): + first_dec = sym.node.original_decorators[0] + if ( + isinstance(first_dec, CallExpr) + and isinstance(first_dec.callee, NameExpr) + and first_dec.callee.fullname == 'pydantic.class_validators.validator' + ): + sym.node.func.is_class = True + + def collect_config(self) -> 'ModelConfigData': + """ + Collects the values of the config attributes that are used by the plugin, accounting for parent classes. + """ + ctx = self._ctx + cls = ctx.cls + config = ModelConfigData() + for stmt in cls.defs.body: + if not isinstance(stmt, ClassDef): + continue + if stmt.name == 'Config': + for substmt in stmt.defs.body: + if not isinstance(substmt, AssignmentStmt): + continue + config.update(self.get_config_update(substmt)) + if ( + config.has_alias_generator + and not config.allow_population_by_field_name + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(ctx.api, stmt) + for info in cls.info.mro[1:]: # 0 is the current class + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of fields in its ancestors + ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info))) + for name, value in info.metadata[METADATA_KEY]['config'].items(): + config.setdefault(name, value) + return config + + def collect_fields(self, model_config: 'ModelConfigData') -> List['PydanticModelField']: + """ + Collects the fields for the model, accounting for parent classes + """ + # First, collect fields belonging to the current class. + ctx = self._ctx + cls = self._ctx.cls + fields = [] # type: List[PydanticModelField] + known_fields = set() # type: Set[str] + for stmt in cls.defs.body: + if not isinstance(stmt, AssignmentStmt): # `and stmt.new_syntax` to require annotation + continue + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or not is_valid_field(lhs.name): + continue + + if not stmt.new_syntax and self.plugin_config.warn_untyped_fields: + error_untyped_fields(ctx.api, stmt) + + # if lhs.name == '__config__': # BaseConfig not well handled; I'm not sure why yet + # continue + + sym = cls.info.names.get(lhs.name) + if sym is None: # pragma: no cover + # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation) + # This is the same logic used in the dataclasses plugin + continue + + node = sym.node + if isinstance(node, PlaceholderNode): # pragma: no cover + # See the PlaceholderNode docstring for more detail about how this can occur + # Basically, it is an edge case when dealing with complex import logic + # This is the same logic used in the dataclasses plugin + continue + if not isinstance(node, Var): # pragma: no cover + # Don't know if this edge case still happens with the `is_valid_field` check above + # but better safe than sorry + continue + + # x: ClassVar[int] is ignored by dataclasses. + if node.is_classvar: + continue + + is_required = self.get_is_required(cls, stmt, lhs) + alias, has_dynamic_alias = self.get_alias_info(stmt) + if ( + has_dynamic_alias + and not model_config.allow_population_by_field_name + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(ctx.api, stmt) + fields.append( + PydanticModelField( + name=lhs.name, + is_required=is_required, + alias=alias, + has_dynamic_alias=has_dynamic_alias, + line=stmt.line, + column=stmt.column, + ) + ) + known_fields.add(lhs.name) + all_fields = fields.copy() + for info in cls.info.mro[1:]: # 0 is the current class, -2 is BaseModel, -1 is object + if METADATA_KEY not in info.metadata: + continue + + superclass_fields = [] + # Each class depends on the set of fields in its ancestors + ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info))) + + for name, data in info.metadata[METADATA_KEY]['fields'].items(): + if name not in known_fields: + field = PydanticModelField.deserialize(info, data) + known_fields.add(name) + superclass_fields.append(field) + else: + (field,) = (a for a in all_fields if a.name == name) + all_fields.remove(field) + superclass_fields.append(field) + all_fields = superclass_fields + all_fields + return all_fields + + def add_initializer(self, fields: List['PydanticModelField'], config: 'ModelConfigData', is_settings: bool) -> None: + """ + Adds a fields-aware `__init__` method to the class. + + The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings. + """ + ctx = self._ctx + typed = self.plugin_config.init_typed + use_alias = config.allow_population_by_field_name is not True + force_all_optional = is_settings or bool( + config.has_alias_generator and not config.allow_population_by_field_name + ) + init_arguments = self.get_field_arguments( + fields, typed=typed, force_all_optional=force_all_optional, use_alias=use_alias + ) + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + init_arguments.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + if '__init__' not in ctx.cls.info.names: + add_method(ctx, '__init__', init_arguments, NoneType()) + + def add_construct_method(self, fields: List['PydanticModelField']) -> None: + """ + Adds a fully typed `construct` classmethod to the class. + + Similar to the fields-aware __init__ method, but always uses the field names (not aliases), + and does not treat settings fields as optional. + """ + ctx = self._ctx + set_str = ctx.api.named_type(f'{BUILTINS_NAME}.set', [ctx.api.named_type(f'{BUILTINS_NAME}.str')]) + optional_set_str = UnionType([set_str, NoneType()]) + fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT) + construct_arguments = self.get_field_arguments(fields, typed=True, force_all_optional=False, use_alias=False) + construct_arguments = [fields_set_argument] + construct_arguments + + obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object') + self_tvar_name = '_PydanticBaseModel' # Make sure it does not conflict with other names in the class + tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name + tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type) + self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type) + ctx.cls.info.names[self_tvar_name] = SymbolTableNode(MDEF, self_tvar_expr) + + # Backward-compatible with TypeVarDef from Mypy 0.910. + if isinstance(tvd, TypeVarType): + self_type = tvd + else: + self_type = TypeVarType(tvd) # type: ignore[call-arg] + + add_method( + ctx, + 'construct', + construct_arguments, + return_type=self_type, + self_type=self_type, + tvar_def=tvd, + is_classmethod=True, + ) + + def set_frozen(self, fields: List['PydanticModelField'], frozen: bool) -> None: + """ + Marks all fields as properties so that attempts to set them trigger mypy errors. + + This is the same approach used by the attrs and dataclasses plugins. + """ + info = self._ctx.cls.info + for field in fields: + sym_node = info.names.get(field.name) + if sym_node is not None: + var = sym_node.node + assert isinstance(var, Var) + var.is_property = frozen + else: + var = field.to_var(info, use_alias=False) + var.info = info + var.is_property = frozen + var._fullname = get_fullname(info) + '.' + get_name(var) + info.names[get_name(var)] = SymbolTableNode(MDEF, var) + + def get_config_update(self, substmt: AssignmentStmt) -> Optional['ModelConfigData']: + """ + Determines the config update due to a single statement in the Config class definition. + + Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int) + """ + lhs = substmt.lvalues[0] + if not (isinstance(lhs, NameExpr) and lhs.name in self.tracked_config_fields): + return None + if lhs.name == 'extra': + if isinstance(substmt.rvalue, StrExpr): + forbid_extra = substmt.rvalue.value == 'forbid' + elif isinstance(substmt.rvalue, MemberExpr): + forbid_extra = substmt.rvalue.name == 'forbid' + else: + error_invalid_config_value(lhs.name, self._ctx.api, substmt) + return None + return ModelConfigData(forbid_extra=forbid_extra) + if lhs.name == 'alias_generator': + has_alias_generator = True + if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname == 'builtins.None': + has_alias_generator = False + return ModelConfigData(has_alias_generator=has_alias_generator) + if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname in ('builtins.True', 'builtins.False'): + return ModelConfigData(**{lhs.name: substmt.rvalue.fullname == 'builtins.True'}) + error_invalid_config_value(lhs.name, self._ctx.api, substmt) + return None + + @staticmethod + def get_is_required(cls: ClassDef, stmt: AssignmentStmt, lhs: NameExpr) -> bool: + """ + Returns a boolean indicating whether the field defined in `stmt` is a required field. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only, so only non-required if Optional + value_type = get_proper_type(cls.info[lhs.name].type) + if isinstance(value_type, UnionType) and any(isinstance(item, NoneType) for item in value_type.items): + # Annotated as Optional, or otherwise having NoneType in the union + return False + return True + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME: + # The "default value" is a call to `Field`; at this point, the field is + # only required if default is Ellipsis (i.e., `field_name: Annotation = Field(...)`) or if default_factory + # is specified. + for arg, name in zip(expr.args, expr.arg_names): + # If name is None, then this arg is the default because it is the only positonal argument. + if name is None or name == 'default': + return arg.__class__ is EllipsisExpr + if name == 'default_factory': + return False + return True + # Only required if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`) + return isinstance(expr, EllipsisExpr) + + @staticmethod + def get_alias_info(stmt: AssignmentStmt) -> Tuple[Optional[str], bool]: + """ + Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`. + + `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal. + If `has_dynamic_alias` is True, `alias` will be None. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only + return None, False + + if not ( + isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME + ): + # Assigned value is not a call to pydantic.fields.Field + return None, False + + for i, arg_name in enumerate(expr.arg_names): + if arg_name != 'alias': + continue + arg = expr.args[i] + if isinstance(arg, StrExpr): + return arg.value, False + else: + return None, True + return None, False + + def get_field_arguments( + self, fields: List['PydanticModelField'], typed: bool, force_all_optional: bool, use_alias: bool + ) -> List[Argument]: + """ + Helper function used during the construction of the `__init__` and `construct` method signatures. + + Returns a list of mypy Argument instances for use in the generated signatures. + """ + info = self._ctx.cls.info + arguments = [ + field.to_argument(info, typed=typed, force_optional=force_all_optional, use_alias=use_alias) + for field in fields + if not (use_alias and field.has_dynamic_alias) + ] + return arguments + + def should_init_forbid_extra(self, fields: List['PydanticModelField'], config: 'ModelConfigData') -> bool: + """ + Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature + + We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to, + *unless* a required dynamic alias is present (since then we can't determine a valid signature). + """ + if not config.allow_population_by_field_name: + if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)): + return False + if config.forbid_extra: + return True + return self.plugin_config.init_forbid_extra + + @staticmethod + def is_dynamic_alias_present(fields: List['PydanticModelField'], has_alias_generator: bool) -> bool: + """ + Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be + determined during static analysis. + """ + for field in fields: + if field.has_dynamic_alias: + return True + if has_alias_generator: + for field in fields: + if field.alias is None: + return True + return False + + +class PydanticModelField: + def __init__( + self, name: str, is_required: bool, alias: Optional[str], has_dynamic_alias: bool, line: int, column: int + ): + self.name = name + self.is_required = is_required + self.alias = alias + self.has_dynamic_alias = has_dynamic_alias + self.line = line + self.column = column + + def to_var(self, info: TypeInfo, use_alias: bool) -> Var: + name = self.name + if use_alias and self.alias is not None: + name = self.alias + return Var(name, info[self.name].type) + + def to_argument(self, info: TypeInfo, typed: bool, force_optional: bool, use_alias: bool) -> Argument: + if typed and info[self.name].type is not None: + type_annotation = info[self.name].type + else: + type_annotation = AnyType(TypeOfAny.explicit) + return Argument( + variable=self.to_var(info, use_alias), + type_annotation=type_annotation, + initializer=None, + kind=ARG_NAMED_OPT if force_optional or not self.is_required else ARG_NAMED, + ) + + def serialize(self) -> JsonDict: + return self.__dict__ + + @classmethod + def deserialize(cls, info: TypeInfo, data: JsonDict) -> 'PydanticModelField': + return cls(**data) + + +class ModelConfigData: + def __init__( + self, + forbid_extra: Optional[bool] = None, + allow_mutation: Optional[bool] = None, + frozen: Optional[bool] = None, + orm_mode: Optional[bool] = None, + allow_population_by_field_name: Optional[bool] = None, + has_alias_generator: Optional[bool] = None, + ): + self.forbid_extra = forbid_extra + self.allow_mutation = allow_mutation + self.frozen = frozen + self.orm_mode = orm_mode + self.allow_population_by_field_name = allow_population_by_field_name + self.has_alias_generator = has_alias_generator + + def set_values_dict(self) -> Dict[str, Any]: + return {k: v for k, v in self.__dict__.items() if v is not None} + + def update(self, config: Optional['ModelConfigData']) -> None: + if config is None: + return + for k, v in config.set_values_dict().items(): + setattr(self, k, v) + + def setdefault(self, key: str, value: Any) -> None: + if getattr(self, key) is None: + setattr(self, key, value) + + +ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_orm call', 'Pydantic') +ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic') +ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic') +ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic') +ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic') +ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic') + + +def error_from_orm(model_name: str, api: CheckerPluginInterface, context: Context) -> None: + api.fail(f'"{model_name}" does not have orm_mode=True', context, code=ERROR_ORM) + + +def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG) + + +def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS) + + +def error_unexpected_behavior(detail: str, api: CheckerPluginInterface, context: Context) -> None: # pragma: no cover + # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path + link = 'https://github.com/pydantic/pydantic/issues/new/choose' + full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n' + full_message += f'Please consider reporting this bug at {link} so we can try to fix it!' + api.fail(full_message, context, code=ERROR_UNEXPECTED) + + +def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED) + + +def error_default_and_default_factory_specified(api: CheckerPluginInterface, context: Context) -> None: + api.fail('Field default and default_factory cannot be specified together', context, code=ERROR_FIELD_DEFAULTS) + + +def add_method( + ctx: ClassDefContext, + name: str, + args: List[Argument], + return_type: Type, + self_type: Optional[Type] = None, + tvar_def: Optional[TypeVarDef] = None, + is_classmethod: bool = False, + is_new: bool = False, + # is_staticmethod: bool = False, +) -> None: + """ + Adds a new method to a class. + + This can be dropped if/when https://github.com/python/mypy/issues/7301 is merged + """ + info = ctx.cls.info + + # First remove any previously generated methods with the same name + # to avoid clashes and problems in the semantic analyzer. + if name in info.names: + sym = info.names[name] + if sym.plugin_generated and isinstance(sym.node, FuncDef): + ctx.cls.defs.body.remove(sym.node) # pragma: no cover + + self_type = self_type or fill_typevars(info) + if is_classmethod or is_new: + first = [Argument(Var('_cls'), TypeType.make_normalized(self_type), None, ARG_POS)] + # elif is_staticmethod: + # first = [] + else: + self_type = self_type or fill_typevars(info) + first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] + args = first + args + arg_types, arg_names, arg_kinds = [], [], [] + for arg in args: + assert arg.type_annotation, 'All arguments must be fully typed.' + arg_types.append(arg.type_annotation) + arg_names.append(get_name(arg.variable)) + arg_kinds.append(arg.kind) + + function_type = ctx.api.named_type(f'{BUILTINS_NAME}.function') + signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) + if tvar_def: + signature.variables = [tvar_def] + + func = FuncDef(name, args, Block([PassStmt()])) + func.info = info + func.type = set_callable_name(signature, func) + func.is_class = is_classmethod + # func.is_static = is_staticmethod + func._fullname = get_fullname(info) + '.' + name + func.line = info.line + + # NOTE: we would like the plugin generated node to dominate, but we still + # need to keep any existing definitions so they get semantically analyzed. + if name in info.names: + # Get a nice unique name instead. + r_name = get_unique_redefinition_name(name, info.names) + info.names[r_name] = info.names[name] + + if is_classmethod: # or is_staticmethod: + func.is_decorated = True + v = Var(name, func.type) + v.info = info + v._fullname = func._fullname + # if is_classmethod: + v.is_classmethod = True + dec = Decorator(func, [NameExpr('classmethod')], v) + # else: + # v.is_staticmethod = True + # dec = Decorator(func, [NameExpr('staticmethod')], v) + + dec.line = info.line + sym = SymbolTableNode(MDEF, dec) + else: + sym = SymbolTableNode(MDEF, func) + sym.plugin_generated = True + + info.names[name] = sym + info.defn.defs.body.append(func) + + +def get_fullname(x: Union[FuncBase, SymbolNode]) -> str: + """ + Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped. + """ + fn = x.fullname + if callable(fn): # pragma: no cover + return fn() + return fn + + +def get_name(x: Union[FuncBase, SymbolNode]) -> str: + """ + Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped. + """ + fn = x.name + if callable(fn): # pragma: no cover + return fn() + return fn + + +def parse_toml(config_file: str) -> Optional[Dict[str, Any]]: + if not config_file.endswith('.toml'): + return None + + read_mode = 'rb' + if sys.version_info >= (3, 11): + import tomllib as toml_ + else: + try: + import tomli as toml_ + except ImportError: + # older versions of mypy have toml as a dependency, not tomli + read_mode = 'r' + try: + import toml as toml_ # type: ignore[no-redef] + except ImportError: # pragma: no cover + import warnings + + warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.') + return None + + with open(config_file, read_mode) as rf: + return toml_.load(rf) # type: ignore[arg-type] diff --git a/libs/win/pydantic/networks.cp37-win_amd64.pyd b/libs/win/pydantic/networks.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..08ef06cd0a5ce069750ca42a345ae909725f5a56 GIT binary patch literal 248320 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%0A( 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 z zTYrZa9)xGbL*L*C9@-(Hs-Hs#X->d7yTq*)R#nITW9%^NhGLhL{$*! zV{$?7jp~H@VHdfVK}CEdBk$+PGatqCtyYrtA(VK>o8lwZXI~O6fW&r@OFymtPBAyR z7i}!zYLdkSu&~P96d+?{rm)Qp_88jFO4 za2oN&iN`)SC?SXK4VW=Jk$1G2a!g3MdK-lMqddl?S*I>i!2)N*fg+1 zgj-ffHiYVr`fvt;P;{LijD7 z9;#S;nar~Eo$|g8@B7FXwaEVi$B3FTehSCe#rUX=R780BKP}o5qc4_-$(T; z2utIAXj8?WcS86hQa$m$DE`CW410B59>qzvd#W?pQPQj(xI1y^+wM66cbgqxd?{^v z==_j&^#TTk^pYTAE}~=n+dWCEaSFX*(2pwgnlki>zNwtPbazi(*f*L)CZXfC#c^# zx@f2U_zm6{oMib|*sdh44nhl$;zVt9BMSE+DfsPLeF~Z|p#xY%|HwyVHHq9JM6?B* zKSPNhhR9p{98qSMaRVPP)_(sBr>&`W3}~yVHUU@s;wbtx>4x8BE#Gf$`;kG29W#E> z6_VhJkG&Dc$A$3z$D2W}PSe6!89`%YsdQr#KWkJ0SAmkTA%+i7@$oe)B)I$JxnKw^ zy{hvmsQU61vhOg&yu!H?gJ~F85EnyC^fdB)zDQN3FvdAx}q^Rr@aJM}pW z-$(8ILHJt|VP|}P8I1L35zovj>oF-`K6OWEFrS+H9I%Uor+{!&mdZ;rBl=1Sbom}) z(rUyMK99!%&tU+3boC~(!}regJK>Osz5$|00TARNA|F=B-43z`M8x|te8^vjOjgLx z9ps5$i0tb_9wzelcZBKw5II>PD>*dFAqDWg z&!S$9juQETLPm<%G(QlUn|#P)L_VsJKRU<`zmnz$KICsimQ=`j4l-TrInakZPUIg` zvEN)2j=*=@;YcSeWuYQwKS9_Az~G?wU_y1_ZiSerypP~XBHsnl&IXKlK}H~C^#>1A z*gs(q*QF*$^pFu+TiI!m>ihS3R5{%Jq!qEMTrs8z_Ob9gAKkCdC&LrZKzs8DG?fzf zD~?d&4iFO37vmrE0YreC%UclpYZ-BWk?Hv<6jSdHam=F2shm}P1@bo`I|UM3i!f}= zZ>&WbNLE}|s*jY6=(qY5yFdbOfIw7kLZ?1wXhYF+AXJvdx%5By&1szL-GoDFoQqJ! zYp{=~_#Z3?70;+D{vZaMPl%&0p}s&nw8ywvsR=ljRNB2I{@-cd`<2%a?4AXhpU~X5 zm5@o^Vy3EgQJG2CK|PcdT=xex*L4~P&&Oa@mHY(rXN641kiWY)KMW^Hw}}encgdJA+VJ+*yJ_=(@8Y4#k~ykql-v_(t4W6-RKV%tIkP z34_fsIEP47?!gr-Vw+SulM^3j}cnh5;Bhe8>3`<{SRuch7CCQmx=NHk91yv z_Q_JSD#M)BxizYw$DNIN=(w}R&(Nm4(sLYUY;or-&P;AP zPr;$Mvlnv0>;i*}ik)!;6<;AZqz5qAT!M3sJ2z9i4&?kYIK!R)7F6yGVHM{Zg6Fcx4kuGRI z9hc1k-(Uc=Z=H`e(}#m=L@x)t*j-@b2%Ac}R0-IMf@Mn^Mnyya1F>EBtAzJZRIewH zDv|#nS#aIQha692J%#+iK@Pwa649sokgpM0LLuim$m%~5Ilza^Au{)M(P@-}EGu#b ze8>qzt^ra$$rI7f!gL!Ej*ko83v!c{y(WFH_AFP)APDGgD1 z{3$7S8=E92m#gWUBso%L3(Z~Srrgg z`WOg-1%_FAfh4B#ub`$8^^3uh;JcO2Ze`i)VI7OOmv-nvM8C(U(EA+!fsBt@7;3{= z^pt+*QC&p8r+C;#jLgI7IzECFi@q8S>49 z7ug{^JYTG!^b^$QL|vN@r{w8(Biv-Ur`o*cpy4`dF2`+@aXz~sv_rea7+`DclKq#6 zUfef!KF8)@ESx;(m}7*-wv$U9@fx9)hC?wzNd$~p2p$tB-+&`H`6LTfNH2`RW(S;e zoP3x@SVmT5g_Zt6gq3B7S!e zjH8ZT<4^;|xxFZ=>)cK_6ullnN|^V-Q=(Tj96_(glwJW0HnVZgnJ<2&Uf+^)6L5x= zH(sY+JIzjWJcSNPclTUAni1j$PKWMmmZQ(fcwK(?BHq$pMXzxIg5xs)=!_})+ zJsgT&&+UQi?O=S-t1XV8S2==1`Vn?@^Kj1b*AeQqmz?{6GxR!hP5Eow9FM;Q^&?S% zc=G985po`Xy$c$yUI#cXS;j^C>ra|n=`|d#6TSXHzu@Xs?*tA-uh$Xa=17=T^csO9 z=(UO9kX|2y&AmA1`0FV3I!4Y@$BSON=(W>aFx%rVK^-TmA5bx?Lw&|RpTE}q=hf>Z z$JLf`(aD&wgL-kDJpvRYhR6BF=5p+RuTm6k?{*uUjDyAfAisjTnBC#^x@Et4ssVs8@CLSnBUnPDBQje}6+S}b zb`p6MM53l=cr!MK4xu(kW2h#y;h-DX!-m zU!LQaa=fASlvC0ME@1?h<60}D3NW2 zg?2)sJ@MwXV%-YTW8ghq>W!Ec(X`ms{T7CIt@~)0GI%dl2<68}`M=Q~;jxpSpV&OC z{JYSJvNG1p3E^fWwV0$>Y2sRW_rDA!W#toF^RRL%tV=5wh1FaWG$-{|UP1i=M)ai@ zs9}_3H&L?o9DA#b)ozepRod!Ph-WDQ15oZgC7>G*r3Bz+%)~@acN1ZIDFG2CQAn## zNJyWPS?kpki01 zc3{=KfJnIp8fr@Wdn4rp9M8Y`h;=5h3Lpmf;Y~I?6O|p`3-lqo5P5B+AWwg1BOiy7 zc#otH*_Ft>KqA5CGRi`U*&`$b=R*unf`2O3Q)}KduyjL^S2p}1D5^h0%7caSeserA ze$P4y-t%jYk8*GkI=DAUJV+9GhF;sh-OwuyP2(o`9E1=E$WWYE{&5#qO!^pIPFx5YU-e%fIp6N#*BM2!dka+Q?w?o)O?fnHoNdi^~j3$8% z!#VZ%nn{G5@h$aa(=}%^Lj#-S$-c4K96KIk0p8~{U9WFp)ctN_HmRv|Qrzb&T`Gr$+l(6VS(C>BeW7mC;sul|&B@Bhmd_W8|*D zpZ@6N&wDU1CNNXz{>dcp0SM4a=)UX=qn?@Pn?Koo;mRVrui5Z1BGWhPltgebDF5^6 zJB5@RifDPvIX3&lP0YYW#(CEB>WoV2>gCm3zGZTq%~kz_|im}&LYzUVXBXW z7tGSrX%(x%63AzS{4#_lh)omh)6x6K-*>2chtA>9nHUFP(RmTsFD{F0Pq?B@*jjor<$f4)ML_9iMLrub3a zC7^y<6BSTv#UN^u@5s6=0rm5eXnppOI(e^Rv!I@{iY*11qU-23n4D3x)Ti`OQoi~!mChrII-Yi=QgyUGMLNZS>oE|f zEhbGC>*yv#1FrvJi1qFxjutKZ)SX2C15Uubf>-4FFjrpf&V1YlUbBd-+@4&=XNaS zp*Zn3{^_p?L~H|{81FqsQ(YsycR(+1?=8>ucyBVoE!ulG!)x^31)ur}0Z^_dlIG#P z4{2yCrj+;EQlugrSP%o@i&jV~H4EMdo8ftG3=v-lvM7;1XHt7@3$oiP72KzkS#TMq zY?cZ>5*jpWeTGAs1>1a$O3)sjkt)#?N2n675ggM0LOnL;i+}%Aw;u_sM6&9uSao(; zZH0VNl~y5dJymHQ51p#??>C@nLL=dK5=Z1mNJ!5C1yiELSCxcm5}95ZNd5G|@Pf&^ zB%HkpffOTTzd&NElJ)#y=aaOvcYP^COLFLR8LE{5^?glLd<-rVnsGJI%BRxdf7Lv zMD*vu-}U}IBztU1q!AJzZvzm9hD?5dL6(P4%h& z5GnuCpQ7cFUyq7slvGNjQ4}eS17~3%^6PJ8mCCO?+8vME=>AMo=s;;vpG9Pt?L+ z5xuEz#4{WbmJyo9IWUyiR(YbF#}mkvi9Bifp$b^L>G0V+k1s@wXG!!$Au5ZmOdz6s zA=i86yXrdWkWG#IkU&Kc$lD2l-#kv37miJbj}UQ8hw(mr`;+pHKCXqLlJ=WJ_DfmCcMn>Cx$SOPl`Vfk5Qx$X9=zok8g;BvdA+yD zaxqx^`(I%&8m>7E;c7Kb^zR*~jOx8tpru_4HSiLPZPX?x52vV14akbj+BZiW;IUXz zYDP-jFyCR`fM11=oSnq8RdF_^iHJAgn6oUKs=53xaAciML4Nk6tmN@;RcQ zaXss%bOX;~pgqC;&3tYpX#DPb!-)Lkd%ZK`AN{D+IMl+-AdSPf)@*LQOJW%grE%y8 z>zd`@E}6)i;|LS^J8B}ognDKU6jxdgZoSFynn9T#gv|2lk-fh%0&t!;&B*zgJbw#y z!pjlxwxXI_%(GL8_JTeq=&zpqS$hq}Qg0dAw}g`8#W68&8R;mimm?lFb4XzkDDY8- zWk)=5IFV8=;pvIOK;QHw8Q=!d<`7nlFOX$~^j45YQX2ue*9ZBEkjIsnqj3hV|`l8IVvK@Xq{*=J%z{bYuXsZTsD1o_kF@o<^SWW`-8yq1p zt4SB6|BBhc{2Aw*hd!s!EI(1^4_>qa^Wk4SF(>B_^89c)k2hmTV78I=&I(Kc9U-W_ zfV6XPazLKJ>ID9H1}jcrs=KnSz!VC{NZ}SmAp=4v5C2UKaD59__IdadL@wjTu+2Y} zs+=Z`JugUl-00BgfglBqWXx$k8eJ?JO>G*}NaJ79cmp(;Aa8?+(`UcS)7N>rudvYX zks6EWuX@OeUV$W}0}>R7mW?w`^#6`<^-1sxI8%#_hrQ$TaePsXj|w8Z2f-`Obc%Bm zu}6APO6@)=2(n%Uqlo?((2}--OeFFvg=C$TnM5yuzwrJ`AM$1*Uk4JgcThqnv1mIn zVO0dbuXpQ+%(JJrORPee98%1eKfze^Ub8V7sL~@6B$4g|pexhDX7e$?qEzHGC!>tS zNwQwwZ>~ho?6}~4>Rg6&PIm`w{{k1{mOSV&_nJi%$eK>Q<)J&wE%TpG?Xo001!QAn z(x)tRss&~~6pe~jRO9)5?@CMpxgzOgP&|A`S_ZOO)Q~=6KI9XZ2(mJfcg1pyS1tbM zX>1EyXmzRnW;7pCEfNu^cD2Cs$Jf8Is>PHqP!Zb0BT_BK;|SH_2+m?Xx*JolIbWi{ zn#N|(_zzL$zRy!TJi=M@C#fdN$#T99&$qyNcN(iOk!Y)02UXvDU->@sun__J}H>bsVqG!QdkxHV@mlrPjA6#_`sYi!Hz!hfMtAv1PS>}AlffD z<7DXp_{^8346dj9kmYDdt2m zs%14X>WdusmKr!f1~PI7p{K=tlMqHCSCBo4d=bbf<>N)}|2)s}n^RWgKCsk^+^w8T z+zKGKgIq=Vkf$fF;13$adaCmj4t?)cTaQpN$H6mVn57JykhYP7Li$(u$NY)@ zwTd&=WPFKN((2hXP>8JGen#S^gCx!OW*G}T1Et(q!3p|JqPq*)dV?Y!222VV_{3&Q zyr}Z+R1QJ<{e@MJTucxA=wqyEhLD;}Qe}jcyKcta3HIGnq^4#CttN=zGMq|_ujW84 z6nJv-<;6GzD|4$i%qu}FY^IRS(yo-smAH^4F}|b6i15u?umP^canuNmLIUbPa4`3f zZ$hn%=)XW3p)bgfiEIrd-s@`VIwOGtEdL5a>-I&i>&tC~VG#p~ShL;|R{3O-M+e1PbPVG$Q8U`jk@cZxE)l$aGZ~VLFF%ftnw} zP(tR1dMx=)mes0e&kx@qB+c43zeP9#zhpH(jON_&I?g$_atW)?$f|{6_3B<0Fy0rQ zia1QK)1+`#A@jCj$~Tv3OPG5aOoXR+RX7NV*(6aEBx1Ir)lnI^(ZPb11x{dvnJ+yXLpo8n;MI_!I#A9|bwI^s6 zW!GxRGqG!k6~eXa2pmfF>I7Ei5ojoWY=I-#wJX8ccgFl}K7au6+jTKHU+hE|>Te*N z)t#{s{b`?kONiPHRIGf9SsayoO%O36-x>JOBi~^hihSR&y4JVlTcoP%T7pCRjTmgc zjdOnamXUKiCEo>1L$-XY_IT~LoT&Tb%9oCkkG-ieK^Vi(P9b3IPE$5g&hs?49*{S0 zNZzy4EGla?>yoYn&q{LG*%5Z)yXJWDH=^GR8SIy5N<8i}mmut{OC>A#XH=}$gh88aVQisC(FoDcEM8kFlZz

T9pXhf2+slU_Czv5AtAht*n+^q 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 F{{(?:\d{1,3}\.){3}\d{1,3})(?=$|[/:#?])|' # ipv4 + r'(?P\[[A-F0-9]*:[A-F0-9:]+\])(?=$|[/:#?])|' # ipv6 + r'(?P[^\s/:?#]+)' # domain, validation occurs later + r')?' + r'(?::(?P\d+))?' # port +) +_scheme_regex = r'(?:(?P[a-z][a-z0-9+\-.]+)://)?' # scheme https://tools.ietf.org/html/rfc3986#appendix-A +_user_info_regex = r'(?:(?P[^\s:/]*)(?::(?P[^\s/]*))?@)?' +_path_regex = r'(?P/[^\s?#]*)?' +_query_regex = r'(?:\?(?P[^\s#]*))?' +_fragment_regex = r'(?:#(?P[^\s#]*))?' + + +def url_regex() -> Pattern[str]: + global _url_regex_cache + if _url_regex_cache is None: + _url_regex_cache = re.compile( + rf'{_scheme_regex}{_user_info_regex}{_host_regex}{_path_regex}{_query_regex}{_fragment_regex}', + re.IGNORECASE, + ) + return _url_regex_cache + + +def multi_host_url_regex() -> Pattern[str]: + """ + Compiled multi host url regex. + + Additionally to `url_regex` it allows to match multiple hosts. + E.g. host1.db.net,host2.db.net + """ + global _multi_host_url_regex_cache + if _multi_host_url_regex_cache is None: + _multi_host_url_regex_cache = re.compile( + rf'{_scheme_regex}{_user_info_regex}' + r'(?P([^/]*))' # validation occurs later + rf'{_path_regex}{_query_regex}{_fragment_regex}', + re.IGNORECASE, + ) + return _multi_host_url_regex_cache + + +def ascii_domain_regex() -> Pattern[str]: + global _ascii_domain_regex_cache + if _ascii_domain_regex_cache is None: + ascii_chunk = r'[_0-9a-z](?:[-_0-9a-z]{0,61}[_0-9a-z])?' + ascii_domain_ending = r'(?P\.[a-z]{2,63})?\.?' + _ascii_domain_regex_cache = re.compile( + fr'(?:{ascii_chunk}\.)*?{ascii_chunk}{ascii_domain_ending}', re.IGNORECASE + ) + return _ascii_domain_regex_cache + + +def int_domain_regex() -> Pattern[str]: + global _int_domain_regex_cache + if _int_domain_regex_cache is None: + int_chunk = r'[_0-9a-\U00040000](?:[-_0-9a-\U00040000]{0,61}[_0-9a-\U00040000])?' + int_domain_ending = r'(?P(\.[^\W\d_]{2,63})|(\.(?:xn--)[_0-9a-z-]{2,63}))?\.?' + _int_domain_regex_cache = re.compile(fr'(?:{int_chunk}\.)*?{int_chunk}{int_domain_ending}', re.IGNORECASE) + return _int_domain_regex_cache + + +def host_regex() -> Pattern[str]: + global _host_regex_cache + if _host_regex_cache is None: + _host_regex_cache = re.compile( + _host_regex, + re.IGNORECASE, + ) + return _host_regex_cache + + +class AnyUrl(str): + strip_whitespace = True + min_length = 1 + max_length = 2**16 + allowed_schemes: Optional[Collection[str]] = None + tld_required: bool = False + user_required: bool = False + host_required: bool = True + hidden_parts: Set[str] = set() + + __slots__ = ('scheme', 'user', 'password', 'host', 'tld', 'host_type', 'port', 'path', 'query', 'fragment') + + @no_type_check + def __new__(cls, url: Optional[str], **kwargs) -> object: + return str.__new__(cls, cls.build(**kwargs) if url is None else url) + + def __init__( + self, + url: str, + *, + scheme: str, + user: Optional[str] = None, + password: Optional[str] = None, + host: Optional[str] = None, + tld: Optional[str] = None, + host_type: str = 'domain', + port: Optional[str] = None, + path: Optional[str] = None, + query: Optional[str] = None, + fragment: Optional[str] = None, + ) -> None: + str.__init__(url) + self.scheme = scheme + self.user = user + self.password = password + self.host = host + self.tld = tld + self.host_type = host_type + self.port = port + self.path = path + self.query = query + self.fragment = fragment + + @classmethod + def build( + cls, + *, + scheme: str, + user: Optional[str] = None, + password: Optional[str] = None, + host: str, + port: Optional[str] = None, + path: Optional[str] = None, + query: Optional[str] = None, + fragment: Optional[str] = None, + **_kwargs: str, + ) -> str: + parts = Parts( + scheme=scheme, + user=user, + password=password, + host=host, + port=port, + path=path, + query=query, + fragment=fragment, + **_kwargs, # type: ignore[misc] + ) + + url = scheme + '://' + if user: + url += user + if password: + url += ':' + password + if user or password: + url += '@' + url += host + if port and ('port' not in cls.hidden_parts or cls.get_default_parts(parts).get('port') != port): + url += ':' + port + if path: + url += path + if query: + url += '?' + query + if fragment: + url += '#' + fragment + return url + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length, format='uri') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'AnyUrl': + if value.__class__ == cls: + return value + value = str_validator(value) + if cls.strip_whitespace: + value = value.strip() + url: str = cast(str, constr_length_validator(value, field, config)) + + m = cls._match_url(url) + # the regex should always match, if it doesn't please report with details of the URL tried + assert m, 'URL regex failed unexpectedly' + + original_parts = cast('Parts', m.groupdict()) + parts = cls.apply_default_parts(original_parts) + parts = cls.validate_parts(parts) + + if m.end() != len(url): + raise errors.UrlExtraError(extra=url[m.end() :]) + + return cls._build_url(m, url, parts) + + @classmethod + def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'AnyUrl': + """ + Validate hosts and build the AnyUrl object. Split from `validate` so this method + can be altered in `MultiHostDsn`. + """ + host, tld, host_type, rebuild = cls.validate_host(parts) + + return cls( + None if rebuild else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + host=host, + tld=tld, + host_type=host_type, + port=parts['port'], + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + ) + + @staticmethod + def _match_url(url: str) -> Optional[Match[str]]: + return url_regex().match(url) + + @staticmethod + def _validate_port(port: Optional[str]) -> None: + if port is not None and int(port) > 65_535: + raise errors.UrlPortError() + + @classmethod + def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts': + """ + A method used to validate parts of a URL. + Could be overridden to set default values for parts if missing + """ + scheme = parts['scheme'] + if scheme is None: + raise errors.UrlSchemeError() + + if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes: + raise errors.UrlSchemePermittedError(set(cls.allowed_schemes)) + + if validate_port: + cls._validate_port(parts['port']) + + user = parts['user'] + if cls.user_required and user is None: + raise errors.UrlUserInfoError() + + return parts + + @classmethod + def validate_host(cls, parts: 'Parts') -> Tuple[str, Optional[str], str, bool]: + tld, host_type, rebuild = None, None, False + for f in ('domain', 'ipv4', 'ipv6'): + host = parts[f] # type: ignore[literal-required] + if host: + host_type = f + break + + if host is None: + if cls.host_required: + raise errors.UrlHostError() + elif host_type == 'domain': + is_international = False + d = ascii_domain_regex().fullmatch(host) + if d is None: + d = int_domain_regex().fullmatch(host) + if d is None: + raise errors.UrlHostError() + is_international = True + + tld = d.group('tld') + if tld is None and not is_international: + d = int_domain_regex().fullmatch(host) + assert d is not None + tld = d.group('tld') + is_international = True + + if tld is not None: + tld = tld[1:] + elif cls.tld_required: + raise errors.UrlHostTldError() + + if is_international: + host_type = 'int_domain' + rebuild = True + host = host.encode('idna').decode('ascii') + if tld is not None: + tld = tld.encode('idna').decode('ascii') + + return host, tld, host_type, rebuild # type: ignore + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return {} + + @classmethod + def apply_default_parts(cls, parts: 'Parts') -> 'Parts': + for key, value in cls.get_default_parts(parts).items(): + if not parts[key]: # type: ignore[literal-required] + parts[key] = value # type: ignore[literal-required] + return parts + + def __repr__(self) -> str: + extra = ', '.join(f'{n}={getattr(self, n)!r}' for n in self.__slots__ if getattr(self, n) is not None) + return f'{self.__class__.__name__}({super().__repr__()}, {extra})' + + +class AnyHttpUrl(AnyUrl): + allowed_schemes = {'http', 'https'} + + __slots__ = () + + +class HttpUrl(AnyHttpUrl): + tld_required = True + # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + max_length = 2083 + hidden_parts = {'port'} + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return {'port': '80' if parts['scheme'] == 'http' else '443'} + + +class FileUrl(AnyUrl): + allowed_schemes = {'file'} + host_required = False + + __slots__ = () + + +class MultiHostDsn(AnyUrl): + __slots__ = AnyUrl.__slots__ + ('hosts',) + + def __init__(self, *args: Any, hosts: Optional[List['HostParts']] = None, **kwargs: Any): + super().__init__(*args, **kwargs) + self.hosts = hosts + + @staticmethod + def _match_url(url: str) -> Optional[Match[str]]: + return multi_host_url_regex().match(url) + + @classmethod + def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts': + return super().validate_parts(parts, validate_port=False) + + @classmethod + def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'MultiHostDsn': + hosts_parts: List['HostParts'] = [] + host_re = host_regex() + for host in m.groupdict()['hosts'].split(','): + d: Parts = host_re.match(host).groupdict() # type: ignore + host, tld, host_type, rebuild = cls.validate_host(d) + port = d.get('port') + cls._validate_port(port) + hosts_parts.append( + { + 'host': host, + 'host_type': host_type, + 'tld': tld, + 'rebuild': rebuild, + 'port': port, + } + ) + + if len(hosts_parts) > 1: + return cls( + None if any([hp['rebuild'] for hp in hosts_parts]) else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + host_type=None, + hosts=hosts_parts, + ) + else: + # backwards compatibility with single host + host_part = hosts_parts[0] + return cls( + None if host_part['rebuild'] else url, + scheme=parts['scheme'], + user=parts['user'], + password=parts['password'], + host=host_part['host'], + tld=host_part['tld'], + host_type=host_part['host_type'], + port=host_part.get('port'), + path=parts['path'], + query=parts['query'], + fragment=parts['fragment'], + ) + + +class PostgresDsn(MultiHostDsn): + allowed_schemes = { + 'postgres', + 'postgresql', + 'postgresql+asyncpg', + 'postgresql+pg8000', + 'postgresql+psycopg2', + 'postgresql+psycopg2cffi', + 'postgresql+py-postgresql', + 'postgresql+pygresql', + } + user_required = True + + __slots__ = () + + +class CockroachDsn(AnyUrl): + allowed_schemes = { + 'cockroachdb', + 'cockroachdb+psycopg2', + 'cockroachdb+asyncpg', + } + user_required = True + + +class AmqpDsn(AnyUrl): + allowed_schemes = {'amqp', 'amqps'} + host_required = False + + +class RedisDsn(AnyUrl): + __slots__ = () + allowed_schemes = {'redis', 'rediss'} + host_required = False + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'domain': 'localhost' if not (parts['ipv4'] or parts['ipv6']) else '', + 'port': '6379', + 'path': '/0', + } + + +class MongoDsn(AnyUrl): + allowed_schemes = {'mongodb'} + + # TODO: Needed to generic "Parts" for "Replica Set", "Sharded Cluster", and other mongodb deployment modes + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'port': '27017', + } + + +class KafkaDsn(AnyUrl): + allowed_schemes = {'kafka'} + + @staticmethod + def get_default_parts(parts: 'Parts') -> 'Parts': + return { + 'domain': 'localhost', + 'port': '9092', + } + + +def stricturl( + *, + strip_whitespace: bool = True, + min_length: int = 1, + max_length: int = 2**16, + tld_required: bool = True, + host_required: bool = True, + allowed_schemes: Optional[Collection[str]] = None, +) -> Type[AnyUrl]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + min_length=min_length, + max_length=max_length, + tld_required=tld_required, + host_required=host_required, + allowed_schemes=allowed_schemes, + ) + return type('UrlValue', (AnyUrl,), namespace) + + +def import_email_validator() -> None: + global email_validator + try: + import email_validator + except ImportError as e: + raise ImportError('email-validator is not installed, run `pip install pydantic[email]`') from e + + +class EmailStr(str): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='email') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + # included here and below so the error happens straight away + import_email_validator() + + yield str_validator + yield cls.validate + + @classmethod + def validate(cls, value: Union[str]) -> str: + return validate_email(value)[1] + + +class NameEmail(Representation): + __slots__ = 'name', 'email' + + def __init__(self, name: str, email: str): + self.name = name + self.email = email + + def __eq__(self, other: Any) -> bool: + return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email) + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='name-email') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + import_email_validator() + + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> 'NameEmail': + if value.__class__ == cls: + return value + value = str_validator(value) + return cls(*validate_email(value)) + + def __str__(self) -> str: + return f'{self.name} <{self.email}>' + + +class IPvAnyAddress(_BaseAddress): + __slots__ = () + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanyaddress') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Union[str, bytes, int]) -> Union[IPv4Address, IPv6Address]: + try: + return IPv4Address(value) + except ValueError: + pass + + try: + return IPv6Address(value) + except ValueError: + raise errors.IPvAnyAddressError() + + +class IPvAnyInterface(_BaseAddress): + __slots__ = () + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanyinterface') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: NetworkType) -> Union[IPv4Interface, IPv6Interface]: + try: + return IPv4Interface(value) + except ValueError: + pass + + try: + return IPv6Interface(value) + except ValueError: + raise errors.IPvAnyInterfaceError() + + +class IPvAnyNetwork(_BaseNetwork): # type: ignore + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='ipvanynetwork') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: NetworkType) -> Union[IPv4Network, IPv6Network]: + # Assume IP Network is defined with a default value for ``strict`` argument. + # Define your own class if you want to specify network address check strictness. + try: + return IPv4Network(value) + except ValueError: + pass + + try: + return IPv6Network(value) + except ValueError: + raise errors.IPvAnyNetworkError() + + +pretty_email_regex = re.compile(r'([\w ]*?) *<(.*)> *') + + +def validate_email(value: Union[str]) -> Tuple[str, str]: + """ + Brutally simple email address validation. Note unlike most email address validation + * raw ip address (literal) domain parts are not allowed. + * "John Doe " style "pretty" email addresses are processed + * the local part check is extremely basic. This raises the possibility of unicode spoofing, but no better + solution is really possible. + * spaces are striped from the beginning and end of addresses but no error is raised + + See RFC 5322 but treat it with suspicion, there seems to exist no universally acknowledged test for a valid email! + """ + if email_validator is None: + import_email_validator() + + m = pretty_email_regex.fullmatch(value) + name: Optional[str] = None + if m: + name, value = m.groups() + + email = value.strip() + + try: + email_validator.validate_email(email, check_deliverability=False) + except email_validator.EmailNotValidError as e: + raise errors.EmailError() from e + + at_index = email.index('@') + local_part = email[:at_index] # RFC 5321, local part must be case-sensitive. + global_part = email[at_index:].lower() + + return name or local_part, local_part + global_part diff --git a/libs/win/pydantic/parse.cp37-win_amd64.pyd b/libs/win/pydantic/parse.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..80090237115f684a7bc6254f1c3f59a4afe693c5 GIT binary patch literal 50176 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 lq&?S8wo~m~yU?z*tL<*P*B-V Any: + if proto is None and content_type: + if content_type.endswith(('json', 'javascript')): + pass + elif allow_pickle and content_type.endswith('pickle'): + proto = Protocol.pickle + else: + raise TypeError(f'Unknown content-type: {content_type}') + + proto = proto or Protocol.json + + if proto == Protocol.json: + if isinstance(b, bytes): + b = b.decode(encoding) + return json_loads(b) + elif proto == Protocol.pickle: + if not allow_pickle: + raise RuntimeError('Trying to decode with pickle with allow_pickle=False') + bb = b if isinstance(b, bytes) else b.encode() + return pickle.loads(bb) + else: + raise TypeError(f'Unknown protocol: {proto}') + + +def load_file( + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, +) -> Any: + path = Path(path) + b = path.read_bytes() + if content_type is None: + if path.suffix in ('.js', '.json'): + proto = Protocol.json + elif path.suffix == '.pkl': + proto = Protocol.pickle + + return load_str_bytes( + b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads + ) diff --git a/libs/win/pydantic/py.typed b/libs/win/pydantic/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/win/pydantic/schema.cp37-win_amd64.pyd b/libs/win/pydantic/schema.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..c142e02d1964eee8d59b0a4ea5f81a2bb489ea64 GIT binary patch literal 346624 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 (&@ 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�(#pA8iuro>JAl<6CQVW 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

  • *K&< zXF0S!K&-LQ^8~^9LGHnq*P!(h(wa{yzC4ArhCW0&q6Jir&@KY%&t%B6h8zq%Yj_Ce zWv_zKiGWHm0@Bu;5i_P@1$#To|9~|J(#whTT7|T?hqOMJ7Nmc1uky6=lWbjZHqpwN zM0$mTG)*C0Ly(xUoaNPx8mZdbS(XUWD~R-`R&JD7^%!YO=tm0V075PYBxLgV6Es$2 z0YjOwSE@Ks%!^C}5J(E}A>D4GGaTKIk!6N(nMqtG(G*p|?B)Z3Tpu_)qfXFzcnkI} z?de_YlR+f9J-vx(HcEUJ-qM~7RdQqW70dQErq9c}ypMY3R%u#OM-{VzW6AWlfk zQU7SJv@Z=e619QGh_CM*DA_`TNP)hS(CGrrwvLbH zrEqG5{HljNEIuI=a-MxMvCwi=@jEggJZJx+`0}C!?d%JVOT|>K_N1&>P111G@JjcrjPa<*JE_3JqaF5KGCu7*e%DkIYR-caNeHAWEjY zW!x+AIoJ42^hf=pWuv`v6jKPI~xTMk=IEPq|XrPCkF1}_ivflDx__& z{Yh1J2^2`Ax+?34p;TqFk>c2F=?Xsi-J)2i34!rm3jDFxNqw%XGC}n$QC$sGItXO` zOHt$n0RelSVCM@($bV_I@`WH|zo90aA-P*ad=lrM!3@M{C(HcTIRDq?xExfS))}+C z>;x;__OPQ;(1KbARoRq>v^r(Ygs#?!-`}R`C!d zQw(btslAU8v^0RgIIoP!_G8ij@JsD2qCSJDE#rz9T|p7}H6$QzBm}-gsAJ8@bPWPG zqi)g!wsX`PM%CpbFbP8;@GKIz2R0}Kwp0Y}c~%Ilrdki4q2u8-rIx1XPlVPHSc23z)M*u04Se2ich^*9N2CKj=$qE2$6hWof# zQ1cyYRrLh5Q3ZYBi;F0%I!!{+{op&A?*0_LM|Tc} z0lH_({s-d}y6;i@-zl7g?;&Cldo@b)+UZ_Liuwvg#w(cU&>afUy@60||0~^-CFrDl zbP3fNnms$gdcL6XYU_Cdey+BOg;#oXfkBv%h-nkiEl#DFWSf{kumt@(5|jpL8=zAS z(A$6~ZuFkxm--i6?q)6*ls3+R0?sEFMZ#A!rJbCUDI|nil6Iw%;qWX8Eg`b&1>}R+ z`6MNL3FBcXq3Tq~ig8Ech9OCVe! zbO*0_gno*l5c(P=`Xy{n2z^>f^gjfS8ND&t-pc~sX%`FXDxw}o)K+MyhpHviAj#x> z9S3xB{h-7FCz%|5Ki0g-!8Z&e232X4S~guEGnHs}MX+vzsBQOwSBNf}UM{vM$9-JE7)(a7Z6aU5SmOXItvv};bXiylgOdist5c8 zpFq$?wGoUsxOj%B(K5c~WHb)4WrddmE1bj?R-Y(sq(M;%hffUNfu+ZddjpUugnSy1 z;JX;)Ygr^AV-#6zVPy4WQGuZ+i&hZ09fN_2@=n4CvUrg*V#dc%zx^zm0|8m=rqWsv zX*-3qv4`|}Ef1ba}x5)1`?gV-q24z})%)g;1N4FU5zcYDilF?k~SGLOGpBW>(w*-#9vC#9~+Xz;}O4@`->vJFB%p?{MsP#A0!grsu%=* z1cO!nj1h=`L=oQri`bi3&wHDIPv|+MXpm48%JNXRgL{Ze|M@Q^H(hSp7|LF}h2gZ1 zgK~`ur)vouGpcC_!=*8yy;dOA=`#j;Oz$x?kb6x@GMGNpFRU2Kal&^SJ>JF0q4*U5|NiP!% zII#^UCWa?UOMbVY9kW_rXABGsfR(tJ4Dv;|$AV(Ndkp2W74M$!B?(jY|MIj2Fa zR%mQuzcz)&&u6U@VF+>Kd1qOHyq%CM6y$Ul z@?`K7Hzo!k?;zw8fP_PZ$$u=xyi>$;9^zMEV;ZRoEpAL1_bXAQ+ZXl1Ty6x)CItOh zE}AQgO2yTHFg4UMRG&AgK%7(M*59Zb;>OnLT0*i`A=f%omkR{4QWNieF;-61$lX(% zlPIVuobw6iSSkFS!#wLV)2bs)raGBx@}fP`?7RJ1l}W{%G)E?d{+KM%g`6LfPZE>m zS#T%Oc_;(Q6;p{sZ-9+_&(#<>ln`XSQm%I?){7gB!I2X>6~r`>Ihzot0s?q;2%ZY) z^Q~zV!scmO2m(2mkiSWd=hRA}@+stxx!t882Jd|i%y^J9%4J4qJj4pUrnMHmmIG5j zuQM5OK|J*ufztUo-M+4;VriuN1Xd{d7uUT-*4=EMOPEBpgf@aOHe-xC?ju4uapuAI<>{~ zc+q(2!NSzM;&bcxUwf2a8t281aIiVHbLJj*&f~|uGyk}E7997^o5#H)yI8HXy?2~B+<!t#>n0 z>F$4SJ*Di=t?>$T`X?K)YR-t9;W@){hK?9EWW}fWAAgH2Vojq`(FiE4mlxk<#@o)D zn3Tl3xE~#wk$ja|{8PGV9R?K_Goj)x9her%iEm`gxe4=h4%Z66Z6*uJ*&taW3aCCR z5O^RoG^3MQ{7L!oESL z=EwS`;xEGcuRL}>4dk0vRV3DVd0uQl1-_>OG;sQo{t!X9O?J{v;WpT|@V`8(ah|m* zg2x-;DJ@O>LR7aC!X;nI3jLGK;<98DM`-uvfx|rOn?I+~Lh--Ua7kav6Pg-y(@pMr zP(@zWns5oBFeTOOmXlg^7M_ly4*>;PhZqWRo}ZEcL8WJ64Z)5;Gv2iN0^tfETvN0- zFY91opSh;hz_gNO$(L&``6!p%s+Qb4NiVq>aB9gNh2zZPRhWISXbDW^O$!z>niew3 zY`@AZKU6O-deZ9ZHhj=IiaTBV^=A2DT&$1BMkj)ZaLM?1JRXU0g0(IZ{|qRsJuQXj6fqZL6n&3iJ18wR*l!3WTkvb zYls;;F*h*j%ki8IBx=-vqmlu;J;Vg84@5|u(ET9`2k_6SVsjUt8Z;>KbDWgDZgRm57Ci{;X>x5@9< zd>S|AfH~7T5V0^df}Q}jOW-vv5>dLNFx4z>k1fKq1|)McyAr7lPe^b}F=b2f-VSYA zt02FwBZeoKzw$Q|w55vSi+vx)djMP>!X;mbzC%sNiR}K0k|vn^=@`SpX1=6upF&5C z8NbVIbj9lF+o;>Y@M_%p#ks{FKQAqtFcw+UFxF7`JyfWVEEwz5twD@ktC7WEGG?!M zzYbl231bNWD6UMixSjA07XmflowB&a+t^L^btGFUL~09-j#%M{%h~&-_;{WRS}8Xu z%V57P=RF|9HAIiW;?@(JCW;f^K~ZZ1hr7H46@UE70cl5k!On?VZx(X-tqipIg}{`< zG6c3m!h9hkSt$)NW;B3DIg#vqAv7_PAuy)qT4rj*>Yto*uspr6e&(9$`X+wV1f#lz z4Uj12#rNRhlWqY!a4EF z(%HRBFyEYbKU@~I!IZ~jpr2I8bd$|6gzg*~Zvy>J#2#r{6I1i(DtF?`N=HoVYOGX+ z-=PM!cy>A@gmGvYM!8wZQ}BZ?DJ!69*xTJS#lnV}WmWs3^x~<_lZu8GPfhPxG#DOM z)W*ENcdD7Ss;EUiepQq22*ysd!Cr!>R8>kNDLq)!fv$)Qwr47V^Rmi{PBCKxuyNow zS~l3T09rlFAHN6TJ5rEquXdaV=k1$nVy~%gkjI0lINCc9XFe>vZxa<+FC@uKC2)=q z!>+j)bc*1gcKOx@Xb*An*j*I)A@KGs{LM<9ez*c;O$o)WUSHjy{BYgaKl1P%ZrqBS z>D>y_?>zy}Auw8%oq=hqBQb=$JZlJaKe}C>b$6#c>stx2UX)0bx7wMxRRJkY#k3Yo z6G`kVIuYkBV5C)#V~pRQA=d14CHY<$K=1|x-v_=PHwJ=m2)=%1S@jq(GQ8TnGLB+1 zr4>NIlGSAT5zWgrxiW6l=c+C*2Z2s_DUBDMj^})I5qQ|T z+MWDR!?+St0`d#j7(>^9gvB*rj5*2pu3J{<0w92sM1=FAQ>me@q& z!8W8+$qL;J@FLT+u1K}|q*~?0yJFobtD-L44AP0EooV$*hT9gwW$#YQc@H%MsK%-} zCGfiSg$-dVNOf+;f7eSJhYp*Kd}A!Rk&UBgUL0OthA-r$@fTp@y>J6xMZo*H;`QwA zFh2$_>>}JKdM7jHp_b5|=S4!S$~^*64fKxTIlDq^GTaD*TZXm=y@~!=q_6ll3 zHVpywSwoJt0Y`X4(h#|61c-l?3R_{S*nw|;e4}r$R5mQ;BKTd*+es%aqo_GUXQ#{^ zic5z^u@(wh45rdY*77)QWhxUIqR6wx>#g=Gnus-p};G`Cq-}Z;W}n4l#4ZjZ=$A6vCmR#{>+*FoZAbXT1jINK~)q${LFR= z2}A{tczntppmxyhEbL%MfzC|I1*1g@JEv1Rh)pn4{vwCF*bwBfpQI0|I*4GRmUY%+ zn8IG%$h6>ewbsfP-O5EP7#L2-iREBhz)CA3(Z29X&Nlcu7&B{m;dQ1pvw)@x3Q=+7 zq@o%wEt_(KSzMkr*X-7$Fo!8!aXIR~vdQ})URfyIhK>Z`PfOxltkZ<3_M2J81lS&o zw>l(=HLR*7Ft)>!A2>G3YJ5flTfBQtv~RkznkzGH-#TAS=RTX}Bz@`JeN-Y6fvIER zfmU96@!{m66nT!ZKEAQ)ThKK9Fi9(FmK)2NPUApm?T=f`E~Xzw_cE!d1;VRb#_e9Q zoI1GsDy}1RR#%_Qf6Z=Jrxm5xvmtlZJg6U&@`|UVCKYziIe1xmox%$w1mnFBqbLH2 z6*rmjhV**8>SU_;?CjLhvc z95=?oVu%jgi9c9r?{|S2Yvcf^Om;P=J|x5^)4CI;w%yF?^4Bet)ZuW64OwD*H)K%M z+q9Oe&8*zMwD>@sUg#0zmP4^2+^B{)k&V~GHrqQjT)czxtlZUUwFEHJ8aE0dAzZSU zK*dwilENjg;Um^79050;i1MWY4dzPhqbNn&nDw?Y%O>NOfgOn#P8>GTqOwxji;m!s zSKj6-ue9JS!X0#8x?!p3NOFTyh0JTA@E~!XFq^ zHn(Wg?A9YWTslh>(+6>=+WC~9EF3gXqLGuj)22!ss)LZ*qkdqDAD@uJ~z;~?bndl)iL80_S~$L<-nA0@v9Ip7XJ7;cW}26A-T15BXX1_ z(B#(HUmyvqh6$#0CY>>(3)XTM>54@pDVOMoMnL-A1wKPL z6^c*^g^nd#Cr1#2aSoucF|$3L?&rmKi^7#w8Bw6x{s0e`^zih5q1OKm;BVjmx8ALl z|4r{>n~L7&|NMW_`+cHmNAHtGb~W@qj6lcHdmi5JMqNWdmq735gGER0!!Zq)uZbHU z;;PpBThgUVSqn_A%1WTzpF@OB&S0=`mU41IF+>cbnjLR|8opL3nK$HmI{Y@UE zlvply;lO<758!n*QsIpYo0UAvm~yqOx)OJrV0JbROD$=8P zIq~OZRWQ$}Y9$LIgt;k3DwM<&V++!Ys=t6mGK{KUNYDGbTZN0a;8Vo9MpoYup8W}j zu`7}BEO+vithB4< zdEh!`x8!iiO>9TU5e3Q1#>v&y9kFFQ{W8r=d0~oXW`jRh3YVUZ$$9beyyE3hL}hr^ ztr(+gg7C_IsTgPX7O*EsqJtShi45$uA)Lur`nroBCG0e{MO=s{t~gSC_q;Y zinW}q5TsBZOCq$A_7aH zfP9I2zPunncK_p53s^Scf$IOUUWns?i!@q9E!h->uSUoor0N3-)^;gREi@7Y*lt#+4yoD{#YJya|;J*oF3yN(BCG{jF6sZJduSv(F7OS5p zl;KiBWt-L%luwuOKa^0HAvyS83njbuB1*be2zvg+YZxmh#xGw#?y8)DIfL|5y7Y*3 zOEP}xAm2Kai{~-@r7MSIrIZ;3S}|u|ix$MG4C5!TTXLwX4dO@KIDZ($a;T~aM)}=d zOvaz0CVa$hR6t-5<;-EYB#&TlZOlkQOF|z(ro=XTB+EGWS}ffeA3ET6VS89$q=?6t z*VH`km}zxl1v?1IKM?0j z4vOq#p=j_BLC%phjfV$(Sql6M{K;jF)F56%8aQ!_jxf zRUSysiGS|P7~N_X#@rcs9e>Q-?AANE=q$6_9ce|UnbwF@bVL%LgXBfqs3_7gFM4Tp zJL)!sYt?9Xq?|q!OW+(J8e;htejScgIWnTrxU9kwTy+eeX5rx$oxpuk94MIG{R>-{ z^i0v23m5@jLe|SVoMZi+&xaRyaZT+#@l76CzOE{iJ5!?V_iHGdaWK}ldP zgxr*c8b=g)k(nDCiwYzjF1;U6_I>+V`xQ+AYjcPq7MhBJIj{KdI_P4gpv2|Ry~m6J zXng4o2EMNb!0L#su?TDE<$GfYw!oQ6)rhOnXnwq+!1ZbqAA*%NkGX4T<6o^{=A8&A)D+!5TPKpw$dDiJp1}TcAK+vGcLqYChM5Oi-ZgD8L7F45HW` zP_?;h1-IuK&%dx+WEX#!F%8Qg{@@AT0X&|DAB0QT*ue8rE}LZ+!h=(ilt)gNX9sXa z{%E@BziK(i;A433@g)U6K^T4y_o)skqGgwZ>L)M|$zSvj^|6L<37WTZ73~ZCH(ICa zX1R!>BP;X>mMR*NXW?_-)O$yn=&|HQ0U4=8`CbRQ3YVNG&_j7nVn?_HN8NBc$IA`s z7hQ+cOuAxQ>>tD&!X+U!gb6S#jfqtGIpz z{d~IpGEAiEV*s+-l~;VIZs8QPuulUBBVWpVDn4c$mRt*R8;KhVEBinIm@11k;>>%` z!N2TqUWXD9f#G-lVM8-!bVH0l{BtH(_jexm7<|)c+_=KMa{<~UkP9pU?*!)77-$nl z@k5jCt5eKqxEisXXC|V_5`}4F@C-SkP$&l`vJ;QHzcsJ?uyj=vATZ5y4lP; zX#FfT3FBs7@oFT~)9?v4-sWJ>^l615t8aQW9&o@fb6{H-!BR_C0|FkL%3yv6PUqk# z-NEq8CvnxjlELhYM~1x~+rJDy7r3iexw?{|R? ze!Ny~bDQ17v3}ttq98nX7aNsCCJQE;%NacEt>b{~lIV#zQB9?PP`s7WQa}MW79ZT} zs%xYUb@^hH?ANO2Xib+y3_fp7z<&iu<5DBh?Bqz)go6He8Dni*mI|f1WyQ2MKVn4- z5DD1HI3wR}SQ~kK>60OvQo8h?(t_0E`9f-8 z54Rpi-4CzJK_iifrJnY5cZ$$Yugo$G#X`9QsyZ+3DE$5|Gy~)~GSdyVJXk78H^j8= zfjP#*v&-qBv1_nJpu>#pq!MG@0f}U-W(TCpf8eOSGI#Wf^{wOT6D4a8w?8n}$L9?+ zn7e$;o{8!K4|4G|<+CZhmBAnpN-gZ?_F0xs-e27T#^TqP+w5@q17ET~P!GJpe46FO z@knR|YLcDdSz{4(C=R4Q>117!2+2DrJtdOr-4rF#kB1OFpkj6Y5oa4mr}6kB)Qub< zryz45PCM?BY$9hQ&O#<*xK#?QoKZOOxD{`!AH@ew(OU|Tb>Pp)yjb5|07}O9eg{B~ z-9{ILOSe*@*rEE3a$72_i<)O0|O4F1Oo(55pf`E;Dv7Kbi}4+ zyso~?D{>FHNEA=nA7aGGi#Co}fVc-i=NbeZBz=h5X0!*!l2i6bMV84P22YTo*NIZQ z;{VjcKrBW_7=-0T&o%k~j*8sCHZ9!N40E^)aU@1RHx1WCksQ3>X+2&4I7v0mlCQm- zkCzXd=mnL~DUnf+?~lNI5Jy3ituL+h@MJ#14nJI8x;9*L0zeM-PM=nUE&z6{=34aa zc^MliFJliqh85tdGn?ab#JVaqk~JVTT-sZfN2}fHn`R5&LUC7Kq7FE1hP zUVNlZxa4(w$`3DGZl_;LUb4dvuX6h=ZZ?f0hkf!v_o}?CLvW+g@}isavbGYo^w**x zIw8}Kzcv9-KYTG<{lGii#UMHLexMr9^O?a1^8FQvI0;u|tfH#7q@1rnJK+tS{SCIp zBol3MIY^2bZKSD<9r??M&P3h7?CYI5EutI%=~Oob=Q8{0O&o#TZ?9f__wD92`!A|H zRmB64DM?sZLd5n+R(VmT^(PvlJlO;p!2U5g#mv9qVl?BCl`K%WzWL^K({*S_Ah+_JxWXwk2m61BR^W^ zC^PGq!j{aNqhm+a^f9S2PT@L8t-_`3|9It~d{>2=6(1P?S?V--!zG(F-6JE`-RLFG zaPDCH&-$bAA11$)j8;t{j0MaXa1d26<*EF&G+$D|p1wi`4>NJLBprO{)NCx~Rwwi5 z@ZvtCNs_c8>C5xs5PolzmRQTOwUYyU8P%D|X}M ze|#{HZWl9qDdtV=7j}oAq>e{76DLZ=NpQK+p0O1TNMxTpeTKSR+OqT?N%GWzF#Edg ztmFTOr_s8;p_SrD94gtaI&1IB$NPAc1%jkY^i`hd3+WSJU^~gK{89_sVvfvJa~L1r z5b&Z+j+fx%1T6J_SSG$?+m1C8EQu^$D&{x25zsw}mH2&(-sy<;=}2;8#uC-|Js^r?*ak6w zK>?E7V;gi@U^vL0c<4n!kkhu%Vow>txm;wplGbXx#}c1pBl)*nn(!{E6;;9s=0F~J znm%7nKJ2W15*FdqD>)Q9`R#`EA{D%((LoKwBg`?QJ)!q99H<0lN;dRjZ(OA`LjUF` zb(v!T%D@Ry%ui9ZWBO-^IL2)WlqLy}UcJHGPMj-UTqw_!?n0`x9)|Hes z@RJ}16?NsK@Ru_$=^PB$@Ej|zD{_^*Xn(AcxZ`U|U`06;Y0S9jTKpm2I^aR=H%!|c zM)hAvFIxnYlP$OY2mF8~{_OvTznoge`@u$P{L@kG{x|$1ArQzqR0nV%B}k#GZ$uH1 zCse$VN{RODR6~&B;K35o;|CjF%(z#GN@?vF;`hD$v;_p7UPP~$j0x52AbE76$T^T6 zNAgObkY|4-&;ClDR_JWtP`1|wpMvb+`%8Q(777uDCf5t++x;*O|J&jZ&QAi2YB8PY z$WyEvX9J?eb9t8*orBc0_;Ee*@5oLF30QznKIb7%xHp%pAlZ#_y}amLS@w7=qfm-q zLLubid+Judh`a6w)F$3jIRbxZg_8j2Q0=oqhd*80i;B-PyG@@D^o6I{$K-(2#AJtd zJRNlybzFOGPm-*CTAwGS|FDN^ydgKq4q zfd7pjL8MI9$4`fv{J5&pq3TGF+W9HG6yp9GHw)3u^NBlMGP#H%Z`00NY*@iL{hd4g6Ymr_ zcW@LZIA@e|2an1EcPz)@o14+s+T7YcR|Qw@jmq&Se#^&(b33VkWnHgNKT1B;HHyqet_0< z9(})fQoE!=oMJ`JRYz8+v|CCxTPZs*QqAqz&RGB&E%lFZItCpNeJWsE9v=keAz%NM zWH!c8(46>}><*t@ha@QrXlz(ph@y6^I0 zby-yIyZns0Ec*6crl$d=C>!aTBCx2)v|KHFS4ZHgqg(H4o+wJ6b5^=LC-Y#pyC+OJ z!#Qsv%JXKVc$)i`8j}29j!DbeBc4Kn!rX6xFwtoUExbs9MMd>S<`hwHq`)Ar|d5m|fI&7GW9vQz%l2g1VoKw6Ph5g%*3O>~hm;76O zs#7>1r}#*H9;#27oDD27;}0U^w)IJO=H68w08$RN$t%UC;v%Ep6!iJg_Bo8s0(uN- za`X_Br(tsSSsr+Q4SqSnak;2|=4aJ61F$0i@dO?8rTjnk-Uq(Q>b@U8m)r{>BqRud zpr8*JBq+oPs1d{_K;T9L+(OV;wfA0<8oV7^Z0lCq zZZ_I>zbI6rwQY2^P3yL%&UKe!x9D7@f4zER{k#GIMi0vDb-#Wd4z*TYUJ`FCll&c;t{V3yFbfW{p z%^E^Jk8M$%=LSey5S7vqxU9hk^h#>y%V8BocmGBa6!&z#5ME7E70Z$4bKYz!;z!#GwD><_%Q3v(g%v2s^T zZ2aAK9?J*IalQgnMXU*{HJmOqCgb;syjZ*eeK*CQ-qZtySHY(b^dX^ITuj6`*#Fx) zV40>f@XT5^nSEv}nSJK-DYiUk_oiI^+IG4a0xRG}SE2{J9KPYNYUx`BzY|E*XND9X zd7yCf`9#L|E3;9nbcvFiJS0K;?90OIiq6&{O%sh3L*g806+%e ztMnF)tm$U0*7T>XrS$_x#|b{I-hL8aCUGnjMwcz%`o$dpT72+RlwaD%eILb6;wDx6 z!0%8c+@G?ZaJz?QA21UP!-C8kf%^UkW^vmvd&|3!Hb(G`?PIR{A@Y>Cm(27LCd@hE zurNd%6YECAv*Bsc32GPP2CTu@TQQ%$ju_$;3)_OZ^v|)MGC1(Q68b?rgv39(Z&L$y zR?>P!J)N(|@)xkMFgzR!wQa$mPV?n zkC-sSyS4fq4C@IPHJ7C%e|ZiiVS0;~E1j{HDEt$-AHLTh%9B9$beEa;13f0G#5t$D zau{5*i#8BeKn!UcNYMt^Ah0k!a{#+fPgdX(4syFN77!h}!~mP-nqe|uAvntfbfsqLqPfiA0u;4T7fB`{-PGtK7AW7{he);UE08va`u248SA~8z;e?& z0e7-Aa}8x|XlDVyP<+F+l;NJ)f@N3{T)*KWyu)G4^&8fqIL+07zMn!KIa5pTHh>(yIo#W$4q~Z?dJZ#RGSfnFYy+ZvA&h=xzq9p&yjQ{R|xI zxvdkf{y7UV_UvAEi?)DkhUKtVuLofXa`WMxn1cUK?ZAn;2VUqY+t&Q-w!CMytl#`h z{(br2HN-NX-rRQumU3&aIPuXdo_zjHEsoAKWAWvg&FeRRHUD0$qTmvh`^q*SZt~Tw z|L#5CbiBN88kWo%NN)4b(eZO@Go8Ys3pM$smJrE!8~rRNB&&8{jO40%;B~}BP|=2g zad@#Z+AlGZ3FD~ezyv6c<>BGlH^)xrZN`)%fv0Z$-DAp6^!uD9SMfm}G4%%? zM7gH}_u&5;lzckSg>Mvy^~W(DDC3BIVAUPS+&u6U?NI$jrUc^B&^)pJEBE4po2S6) z>rzkW<93CCLfm({2~*uWrlx@#I(fSA7;0H}l=z~9#o;Qp3L79no6 z2qLt(zo!G|j*+Pi280rG*ChBb8*oZ|*#!-_ymq~ScayMTv@-~?J-bjCiBLE=8L8qg zCorDo3u~-~fm20*Lzx1q~ z{p{hU#whTMD?wJ8#0&hiw4&(l>~EP42V^(7l~5 znT3f)m$7~Jn=kR^-K|r0kqgzs&+0|NdW4Df@cp}~C}ATLD1EptZH9$!^t4e)Fs9VQ+4 z35dIz+G)g;>-$#0zuyGMeTrn`aOr-tzWL{PK^W5FCd0#8ZF;Fzwt=GCSdtJ=zEJ5Z z##1VjrO#FY9Il1|%pDfu5ZY2py&F!IVGkBQTgvDGgD%D2aYca=|#y5Ej6nRHA%=VgLodqqDGPhL8{MpuO>KlgFI|1=Vw89gM0G~Tm z2hU@7&E`^;Uet9AM(be#c+QrAY|WJe#A!?hY*%e8ugZJ|+c-7-VM+)7KJx(?$wTnw znpoHj`DJ)7q_x|($LG{?-xsOViu`HOO)Hr@U*Z)`kbM@OwUkke3ZsY^4DJeDeP>fXiW)<$HxbgdwlQoC_k{SU2iXV8GzChQXL}yP!T)&z*#5Yt> z;F;cs@djE8Uf0%?z7xGz2AQH@x`s~K&^k=83B$bzb~{41L(t~72tHG4w8wz8#>4t! zr~;ypL@IPm_D$J5D@xzmWKAT1!j+0>t5vZi?aFzpWr>Oc6jYy7qE<1QV|TkwGOGYwTL6 zNA6_l?;z{+3Yl}7PqafMBT>;7kXK*1@Dkgt;>~B+3)VjMAr_PzQe*`UCLIGhgSahf z{KZq=ULut7`zJv`_2YQ9{ya4`#<+yv01xeQ- zxuHcJJw<)0pDt7xD1EttT9Ujv(j;JP*k&VrY8IGbKk4~VZQuGH(=;HZy){Iu$wdov z9}aDmK71QZ# z73$IeHU$*@kcDUt-5k*nT>o@xE0oD>c(`|=y`#A%RirN^%S2yf-q0xhDF6izupj)} zAUXEY@N@CO??HzrOG~(oz`K`IQ)D7JV%bQK><1j=NBl&}q;G)~4s4@?EVtqi3xxBB zu%UDcrRXD6`*7+1rDZ&l{02O;IO#hIWuT2=VoP}(XVi!>uneUE?&Q~)MHgiL2t9zw zIrx2I2~P%PR+8`_6w&uJWo3Rxh|PXO|K41S{lJfVW@8?MFDCn1W;^!CXfC~YhJ=tc zv=jFiquGY*B2BbGyy*Qbm|1fLQw5XN&>sP|5tu8OEy#*1WSDZCI#mF|hZ`}>pqMre z`r7mmk4Gv&qP9M8>PCHO`oZ!(^dINLuv@5tMg%-v^G)7spXp$ilR0$)`C^Db#vfoQ zhxT`vse$({BLZ@7-)kI`5oh%H_xUxr^m~`FoXp;Q=u@OzSSScVK=3=R$cG>Hps|!b z^)Tb|aCsl9!f=sO#&*}8;QZ-%bUz$;ZE4(b{}e%FOP6sT*%MG_;rPIhiXV86!S29v zDP8gi7R)(|+mof6nR)D=Bbnkih{?CpAv*TY-;*msrsNBLJ{^+aM}1oo_Hwtgl8|*= zBnc!DznUa`<B_pQ>5Scpv5cd=*Kf+DTN;vc@2)$-(6J#NH%P;UfgYTIA*{^vid&MFUwNUtkzG!ve8+2=R4-NQh$U$0}Wo(m)Qd}&H61_&4 zn%!TE<-;?GRnR;yAI`+`A*~*!Z^tMlNaTQX`!?s?h#Zy}JEs~sR!$j=C~vCB$$^c! zsVXODnl|BC-|n1ZXZ-A?ZEzcyk!S?_?{>$38a;aBx#WCzuJH&Sz6tlgvk=`BH(;T( zIV;RJ^_Vw3?l){GsKF-p8!f+h1A^y5W09gd?v-qW`D{QPi^E`(?awDmSCAGTxVy%z z4}X6tE^PcFDWkgQ5;ptazLXRb_P+~Xz%z>eLd_J1#MAc;J=NbA$16@Kj&=zSJ=xIYY^ug!|9ge>40KrNJ zXdY_~5XvlM(>y{!!+gX?R3D}c0tduPm%y07^kQc^mU?PvR)Y>;^MUJu_rgHm#j}nT z!Tv5NJ&zh|06eD^a)MMccFm3IFJc?1i4uDxb!qO!IplN7vDxQdB-LvpAI~R z`=QC@Vs?s9D#+43R|XFkUBQh8bfY-(ez^30f%Mp?bU?U(t6%}eoM{#oV_zsJZ1~NK z6M@6bgQD%vqUrv?w<#G29HGqqz?`pQ?e`=?BYgh~P0o3({St|wulK)dp$n)V{0NdY zC+UY8N{};IdhKG+fgK9nB|HQD#3b+b8Y(-%YCx(Ihv z;gU(-8h(1l;fWvKl42r&)tD(KiQ_!_tr19$Z99d??mv4XG%`}ULFl} zNS+ZlU5A|=sS5gUJ`QiB9H+}N_f3Zh3}kj7DSmVqs#Jlgw^2$@6{FJ#`-*>;&x4&R zBevWAJ7*~tQ=YG_c_#C30CErkHc=~+4k-2D80cS9^9A354X@2d_2LJ%!mzK;hy zV$T5zf!#YY-33zy@&O4Fc%WL*ux=!C30|SmGVGRkoGM^?KRAH-u3I;KxY5Gtc4wBVdXhH&oR;_>+M^4D}xv{(uBa zHDu{wpho%w4S12c4c%nQSO+$Iw-iXHug!F$XPE-(zYml+u$6v6YZGg1xfP#U=}9KFj*Csi!}fFGtL3aB74vqQb7v z;_FIBihb$>bXNP{(1XJpCvo&=U!Ae1WR;-3RowY`I6n>X8yF)^qLI_*PyBj``|9_duZkdi`V}tHOkV?_t5J?3OmpNw;4y3Azh z8!#mp0|gFD=A~=w7AqYsVwG_qutC>M&%~Ma4p9Ty(pEiD$^ zjyUze$r`%Xp0p|}&+K#%jM4<>YfzGOAoFn$0fS!}DMCPOAs}ya>bHP8>f7-WY5FY- zNYD^gY{3_-bMxcK<1_t=^vZ&-U&cT3O)Gg&%<9Kb0OhKYp!YL)3IA&=AoSmw4Jr9^ zyrenmYS`s3gJoohs&R}F;d5XcJr4x_jFSGq0u+~N?Z8FIdOGl0kxx;xPX|up8}ah# zz<Z%RR(%xn z5yAB*(y8EqSQ%Q`h86LL03(QtQG)$-C9unv1*RUNm2 zYw&$dT}g1w6T!!5_)2H(gO}oAO|~h_em((CTumrIQi8!ZNAY+uJ%aPN)`#K>2o;>l zh7Vr^!ldzY8hgf34~q}3l8{~VNw=o1@lh_f^-mh&G1C9__IZA0GR%_)!ODC z_nm%|Xi9aHy6%k3qp(M(7iMn5GtB10I@`Z``1Fj-VLTl7af4I`#(x9E495R=H9Q0B z11NWJI8Bu<$RzZ`j7&SeoPO0&KXVImGCT2~WD0*F?Dk$SVza(;gLL#%<~bNaSeE$* ze&$QhVbc*Dz-%f`%{;o0`iu1_UU|`)Yj5LW^?ed`xvw8)AXqUuT+{~JOdUG)W#na^ z)zAZ(zvZG}ko5DfKlrMN+lm%_j~8=8~bm3uZ8LFNz5T=4Img zVJ7Gm!`IV4d=Ow|P^4k?Z5V?JD_niO7dt2Sy)W}GU;;Q!c zrRM-Yrz%0^HLbg;w^0DH_%S5(NGTrw1j$o@PWqGKyn9~oz>{UcyyuI*P>Z9cg?$Z&3!e@4&nvHA|2#G)+*pVMjEMrQPUSt(uzolJ zWB6pn>F;4C4GxMVW!{1P6 zU0y>|$oIcW0m{I4#8UsCIm`#raOa#g(?`2uHcC_zLkW1nKh+2lFz2$A0_|%11#g{16e*47Z8dF_9HK zy!fRZRC~jVNNBfe9`zq4u#g-A4%7$Ne=l)L`&jT0_xBJV^f>VF;?L;1$6R$s(SfPz zJ_!M)`Xw}jz+0Y6aAW#2#gE|ZG;QS>c%kX0SL%6eFZv3;7C-*4^NJ7rw$3_v%1$t1x0MzLkX3eFnUzpj9Q z!&-V`-HG%q1A)6y`oKv3(}DNl3*=h$2iowh_QxlzRP})o>*>I|@d#$)#SVPKcsXx$ z^QB{(*N>$!zXC|*(Z_lW~z1uX#XUOTzXIAZv!N3oJqjxDb25 zGr8{(tVdmTVrW})$p7Y8L*Db)wAFCw z^YzUqZXB3L6FDVtt2CeUP%X;_11p1JMRt=LvQx$N_3ZgSZl z;(u5j_g{8AH#zQpN6(J;%f5R7NGZ1#{_w$VHDvSdIQ0rviJo(0TbUP-bA(@Fmx?K` z3$OW!DU4wamRNka#GGslB;3LsN+YjfAJ9Nud|)?LDz-&3DNLXdq$=n%AaRW~&)i=P z#t-AxgMlZI4_f|GfAb!Lfy3YIA)PLj^m<7{l6FY?proIW^mCGaUDAxCzmW7sfnOYlcNqV28M}lCG9CDCv78y-U(Vk{*@xn52(N`mCfGN&i~-`w2-8N!lgpdnFA@ zdby5GyMOZrJk`y_3X^kzxdNqU*27f9-t^z~0`_}`WESxJW^Ju2yb zN!uj7S<=mtUM}f;NvBKt`d@1JFG@Nr>8BL$jyd#>5QF$N| z>v#)1-0%0sQsLJ2SVv2^y{ol5ThZN~S2x!tf~Wh`743|LJEL)eGBup3$}v75o(7T(w=SLt#46&N_b3!w*k*l-Wwj1 zYifGA@nd+p{FLyRFmD5%qddXu=Iz2|+g=9JYIOEvqPwk|_(XOuU6q{mg1~pB|FPz=pxytLhhKpyx-`AdM3pc~&rxMZjF8s)! zTb^9-&nmwcIMH7gMz}TI)vQ=r^(+4WZNjbG+g<4f`OCsFtW(j%UN#eC3cT$_JDH1= z#X`ELgM}Y0J9$8eq?56h9Ktbn=j4x@e(0yJ*wvlrga_Uq3u|7byV^<6R9#OTMl9B1 zEvvlhn&e7rFHl=nxw@)KI^d?O8%`MUM0-ldFw~N}{H{CO<69{1sLUK|*+XKbzCWnzqq@gk4^U+mz`%}QVDr|G?yxLtIb}P{o{hXqmHE*+>wJ4vXoNi`L zMc*mfy*do0p9dYTd7JoM`!>s0zs>U3z0LAhRXHluV;(pWPsH%Q)!Y{C+8eW4@W7(I z%1X6C>)_hc9Wkpp+GTZjb@W<$Vpd0Yv<0#c%hZj$7=5v!!~sbhccoHcL_a8-AW7HX zh#QpO+TIb<^530k-`n10^5fCwJ0anC6^|xjUD`aOd^`%1$T2^agg@7Kg_E&pqPY#t zG-sn^4U7KYrjcK+ed}_LI-(tkShR&Y66>Gq#F!vK{K4WKfovoMbvK zWt7^JZimCvu{gnR?oG8Jyhx-mC?by>4x17hSC>2~YQb`60uEUhL_oWjn?^Cr!Q6Zc zwLyQ;l+_W7CWRXO+}VxCSWmP$)zPbr-PAUyVuh88-Wf~6Bvs?T{Vi6!JBhA!cSSoa znA>zG2n)-Vv?}Q3-uAmv+*FTN*BRvi=tPK{-3b~{Tfmzv}v|C$g zEuR{L&RJ5x(R$-S}X(eN+73&Ht{Mnhn0=X3{ zI2R&eHTQ=1C8F^P(i!&XD;e_*g!4+gWofk0)Lq$NgA?%7gaC8&sRDudiggxjK|0aJ zP&o(6?E#m1W8=YP8BKx?G#!vDJ!pmAO^vY)N07+{$8V=#3WL++nd0q=0-Fq#h{h|X z#%V=?mSbw;~-jXFaW(p_X~ot5{uNNL!k81$vHI}w8qfU1&}U_-!0!>){R zok2@Rb+*aelWb13$5T-W)r6j{uzq$Gb{_!V-qjN8S(QYP#V#ebH%DctFwxo zk1<}>vfWG}0K1x~BF#@RsNBsX%9i$KHh+m;adRyeHE6Y@$vWw%k_s5&zpuN!3zkqg ziCz%_X@Jg#&}O?fZm5KY*;YV-vLjj+n_N3+;3KS0#!xn~*IHwn%BY2Kzd5#t*aC?m zi($$edtt?TnqzUo6zuF}W6~DAD+WPK#^5h8%cc2b>KQ>@w$n;N7&}_v0a0A*73j_6 z*QUtoZnZW>ld(F}!b%3eY$w4Ie6y`LcSMs}Bai0Odvul?ioryut%(?6m}n=#fGB`< z81Kg5=$nDfo*3K=Is={9+eJQ#v<9y_;JrY44r?_WqrFw()z^jBtX8|)5G=G3Ri&^^;#BCB1m{^Dn)UCTS-cD){6v8|n2#0On!Az5X0U_Am znTEpQ_ot&BBnam&OFg4O8gX}d(sGEbVKv+-rY{kn*>M4yg;#`BPFar;>gw)VMX;D~ zVKYR={OQ@ar_$u{4UMQmIDF^6?2PN#H3+b?LfA1%p0Wv71;RVr(ru=Z(H2bzYgzmT0MzT)T7YF0F%XJ~243!|5ec;_D6tb>x`t+S8rxf=i0Qr_!h! z&TtpHsa*uDn8BKm#Gk|!hDKooh`OqNEp!sewQU4^5zT=ioRne;m7?0NVcMg$12VLZ ztwS~+_AUyoOdy5^X6@W%xm`A-Qgu z@yvE;hvIq^6YGgJQ)En53H^?CElg(4PNTE-K(! zZ(E7tx2;U=x#JFQOV`Q93$PTwYI9_PjlJdX(0}www`2Upnhv2>*mxJdN{k7a>88JQn}0o06Hd#(d2`5S1ggFzz{YA?P&i&(e_n5 zZfNO-nV~UFCl7&T#==pF`H5db3Dt$S?A-C5y6wB_8a>w+5U1^F2NHYQyP^rK_0oI} zq=r{ziP-jfi!36LK}*N6kO>AFKGGzv#1`;wu65dIPkU#&6XPv}W00bbG)W{;A5kE! zJR>qg$Y|ZTYsYqzBf)>{{pohdB1MlB@>ME~_TaaYG0X+UXxMcpKJD?l*7B@XD*|pj zW8Q|$F1uS=6L3u_^0KFMu9^LFc1fG--MJ5{g^pT)*V*3H5bN5TYQy&qgkI85^quA~ z+PlJbq|dZc_IxkOn?c1&0BX;!%TQ+&oJ99N%;@TM2*3qJSLvxI1&+rknz<8(n!mGQ z&aUEU4k@)OmQszkg3X(Pb(?Og-@cV_jCHkxVi>iXKpvU+WBa!|g|Ii(>lk~`G&^|| z;f$q0JNZ+|HP!AeNF?A~dL<~p(`&GJSKiI&-HUvZ6U|Vp>EM~@?zMhQM8>ipTx~P= z48>I=h{AlOPT|jZX{ax`8|F7)a)e2d+{n#(|HPi&FcdS&e%5?3aWBpN%UT@FVIoR! z&2-oftj*yc?CSf&n%lSwrai^$5!mX$RyTTi`=10R3Vx z#dryr)K}sX1<5Y`a!=V;UQhG69myMLA*K^cV5p*WYuU9wg2`}X{V3TMr71vV=97u( z9FUn!6y17x$;bup-kpF!*bAIs53X_A1A3-5n`O=w^M*_h^qlR|E?5~Fvr-x8B#DLa zl5(&|n8vLnt2~ojsW1-|Pa78Ey?gwUl>-(@MYzyBh>Q1fD1S?$dw;A;lm^c?(|Rhqhn5&B50r|A8jxNFd=g?C>zb`3)yTsMdE1F6n zve>qVO1hW0ocIw>&S&jSdqv%DJy>Pt%qYk9qwoVUXaP+w$xz@$(Fn5zObo<7VIPlfQ$?rL=k5WrHLTW|y+r5uM zEXLPzNIwR0STn$63igDsJjSrTp1|Y~NgdjQ>$DrkGxc+7f1x!34^<~$H_NO;T6H52HzOim+ z?M71FZA7On6yh=3YIic0xFwpf1L+++CmEGlZIrJKg%DYU8|rVW3p21?T#@ujtDaWP zT|73%{~D~?BuZxCnPN1`#uzBpLtpR*Kcyp0d?(+}nfOoRy9-S=)HUwdt`Le5ZdwTN z9;@g)mCu11o*g4820`;Tq(kPt@%Q0wHkT}SP(U5fp@ybgc7n<*>6v|z_Wnni&$8{Z zV5I9CuTA~}p*ifGiR(GAN@7l7n>ZY_xqEm0W=0nOd1_VcBk(JSDLr2*=V8jA4fT$kR5&}-~aIZbPpM|(bvojQQ5LXQ2%{;BC z=e1vqWX-9Ut**EUVrg%X;y6Sapm+<-weZX-8p-la1~AgF#Qzixwzfl0i( z$+R1^6;{x(ke=$@4&VfW@TL^}vhAN4<5RxvC&WAD%7?bn6gYxIppuT~a_ULTx%EtB zjCNiA5^)KlGNUbe9AaWuf~)0QM_(+ov8+*j_kNk3J9adN zZ>qbM-qD}L!rd72CNR>s>DCp)+)zu_fVYNobCR&PRG;X0wmy?|=5{oU-NQ-8ys=H^ zBw197akWo22pIcCZP1^lgWe-X&!8`46f@DXvCY&=w&u2r=N#85@n=YjMLTzxY$?H{ zKcNS)nEy7LxJW-Lul?0`c6IM_`n&cxA_S~@8N3j;<{;*^ACL5xnxc5K)&YU$Eod*F z#0@Nn1e^Ufxf6kILUb7_kX#@H#y%rwhfyy_KBc|w4u2|fBf#XKeLiv6l>XWwubXuU ziAM34#Xomnc%@aa1(QR(a*B;KFq%n3Gcl_zm5L|VU3sOkZ>umkNmjPSI^tNB#T-#( zd-s)T+Szrb=H!)^Vt0f@KC4)WR)Hii7J55icHFxoQAei1foD!=uj!r*fyh{C?a+I{ zwSQ=+-_@x5wWG`8)~u!a8I58)Op4X(_b+qp>`bc9#gb*T&ppL{Tm|4?*Ir1!wAs*4& za$B2uY}sn!a5L{fWehyTr`tExnP(nvh3gs`>O;HgcTqX*0W@UegDD^Sjm=qX_vq0z zPvainoUJ0cyAv@V*xgMLw6rJtz#OWm=b6vRCyw6;pLEyMJ37HK%*kO0%QFgk{}h#n z-OLp{%EX^@-iU;h_W$XVEJAcyxnsTdI3EBQdoU#DKTI&w)+P44PD4%cm$64!mC=if z+@AJ@W`Xdw_eNLkuU++?s#Vuj-o7e)2LZ)62XnIwp7blLv{XN*17SN>FzYhxOi>PW zF+(`?S?@BzTnfgg+pMS>pMmh}P4kC6&Q-}gMv0_nwfYX>b5=Vh7gKCibI;d>MAMQAzfS1oD3Y2m*#EkkS5`lj`j0f&|Nz8R^7^pflR z>f`tv$0xepulC`y^#;FM_b$Kcz-OS=ua4mJU3?U7W2jo@SCOl6<4+;_v(c{(0LC?& z{OTd3TQ@Z}P#LwxuPRgU7Ui8fZo>O(kZtnHtE%ieIuC(*2=J$)49!MG%dbXpOqTgo zHPx%CYHmgRSB>XbYDfH;>CT<>ZC9TqHM-%eCd? zHFCK)@~EpCmqsqvhrB*?v@$_`z)RvcmEq^|ySh74o%Bp{a69Bd^7rrG-?E1$m4<&y z`j<#Gm)F;FJ0m%H@8Nbvoq3JA+*nSYE;o+6(Rpa6N8@w+lW3RliRtIUKlQ5+J!_h; z##ePa@=(R(t)aX)@*KQb%Xxq4R~w7%a>QPAz^`gPj6CUn&DMZAdL^FsNSNCfP!Hou zl1f^iBKiylR0K(7BAI9|ueu6fRRnoQvw4gY<)7=%hkG!lubx)5@Zx+`{JB{ZGskBP z2jc!nUT9iX2jsK~0DKqZsS6&NJzN-3YRQU1wc?HG%6esnTKHnVx*#=E&1x)A{U4}B3Oe`M1XmsK5g82MMDatIov_Q>UyHL%0p=4tA zc;RTlaDIG7WO|6|l3`zH`PFRSC2Gb*V3_hokhjD?4Y)5=aZv0}@p+DLzq~+Q{>p`F z?u!?x;?yEFt7WnBH(t!|{~Tq;xqnbPlu;<7P=@Myy~}~EAQMt2Hy`u@oZLLfkP|Nj z&xw>&37s3~s^b_TKZnot4*}i+)PJKu&3$E-Dt@t0<)184eq0zr??>?oQW>Tf z`i*zF`6OS3t~?DpTNhUeIol)pp*@B6s4l@&bU}#=uZ63NRC(%JRo-%qDqp)+mHR4{ zO+k^xh!6dnou_8MTrisN=wBR{J>G}UqTd1T7Z<3D(cS{IH|J!bDoCM!EwlOkL6oVW zGSol3Q+P-9viayA@^kYv>}(mxhoilPi~Onxyq!MmkC1#bZ#xQA2YA~8UM>NDFL?0+ zH7hk&`N7|bj{?t+DfMZ_1MT5`Zk}>I6a7lKoB|vLI0|qSU_0yi$_i9jONlCLELLS} zXRES@=1p8M4%xE;>H=Sdf`zH_3mtI_ms)a3fx6_4a#i}uGBx|f%T#`9Iq0-fjsF>Z z@28Y%c$nx!_$_ngK^Glm*iJnOJ4O7srchn;M!s73%1rdRK+R6gQUxuA==U5o{AHzH zz~|EjF5czl!6fIDp?<@I0+s?Sh4KoplnWNi!v|q?sQtx-YVjL>v>#BVFHTo;QZvwg zKH7)Ooxpu6_^kY0=@;JT=0SOK%24}|6Tnhb7O)gxDHkl?`~o$>0-42HZxaKvvzf1)hu<%+KuWG-?}NwjDSxIar5U~UlrL7n}1Ejzj@Qu zypxrX?<>K#wQ7{^`CQ;rg};aXEd&kTSgHK4tm3>1@W4pM?6f;QHvqViy_O4AX^&NVZ^9-3n z9fdl$0TsMm2A#^SqxrKF{JC*Te1jm#!i)1%q3=Q!7}fY3K|4#TW~-_<=BX90l&WPX z=c|iT3shlCnaXcm2)q`l%3FX!{ZOY&98+Igzvg~Ew~@vJY60In}TPv!6N zk4{71m$VnE_LI}qI>_jikkKV6$Y9GX6psQNr?x5kRF#Kz z=8;dIqvj=YCD&InjqnOBSvgy+gl<+qH!p&2&O13vm7x8C7T8+UvvBj>4fq_D_MOk# zPr_F=k}MRcMXPNqANQ5ysS@8jH50U4a@A~g)f=zHGK?Qi>tC*6gryKz2j@`bQ_fb+^+*gW{#mQrO^O;hj2r{<$X zFVcC~SiH;4M|)@Gshl#t3k%eRsbX~@{LO{16Bn*6R2M!pf82*FYzxrWg2eQ=E;AEl z@=<06%1l=mu0|QQ->~70Ghx5;RoQB7zkP*}uRZzW)2q;jiwoh~pxZA7Ky$=z@LyzK z1EA&LmTBr4drJ;U(nP zmZ|BZ+OOr4p25yFzOht=Ub$2)I9aadrY=_njVr*93N?s9!vBZQL2h5=Jo{$mshN)i zB7W)v$FliqsmM+tc$*KJ`5R}eiBC;a1;?hTQGwxn_W6CvzX$rZocvrV=rj*>xiM#%GwX=;XVvDTlB{t4C}#NHRb0$QT~b6RFXZxAoN>k}1saa$sX=^>3r@~wU$I}e zw|Z7YmxrvroTnbgC&T4Q=W_E{mm2elzq$@^82pz!^&&ot{!nNEUro>dh))IOp|9>| zXB{8;58%HTI0`Vsj43Efxc zvv2QowHJ66IB5p@48E49X1$)L8l;Z%S^6E~eq*fIhjL%Wr;o}K9)PFZ`6Pp>d^P7k z@|1F3h$Ns{RPu8R)-x0t^v@jWHL{U;}c|?Kz5mU zkGS%zchF5oS>N1zH5YW43mVPcSv+18FN|pXX99opV=Z(MddReR1?>bdw}DUjpq7W+ zXVAt~##aElyLx(+@A~QLdXg2PE!zaZF9v*?E70;VT^)zsE|}w2hXlU!*;j)0A(tg< z5yz}99A~+QyaifN4|Rqw@T)JGdd_E-pXsVSI2aS&wobk@l#L^Vtcft|6rUR*xP<5ZD6_ZPul55DrEHsbP7C>F;xhX^G$w!yB}9gb zU~ASw7FSOX&7|08DdJOs^G6%}YQ+uU1K?2Vn}+dX2=#GmcIn#3RjF@CO{Zt}Er>1G zg6w%W;Ke`Z(@?QR=#+%FHc?{40fX_HSRfw&Aj@0Z-sBDg}xpoz{ z{Dq^PE!V8A#Oj+0_14oJD0;ygCENZ?^8jCQGp%2p3HkLLFK2-LkKsM?CjK;A?j-X1 zkY_pS-t##6j=ZVfNpPS)e){8$pYmwDXd73%(0bHra=zH3pjq%C2y-D~>X$RyB#|#x^?NyNfmj zhqvO`%%=i4XdT|v7Hht9CrIpO-RQ>GB`D>NYwYh~0%h@}o8B80~4a5#EIh5gL`+m+kj9+OyTH zzAf-*S9l$kM#=@No%(7dd$agn;($3T&fUM6c|sf0fE<3DBGlVJH)hr*chT(&;gnL3 zIVieQb_~Y|Lpp{+y`;S|;hShLV=$U*!)%SF$W1XK1D)pnFmmek4se}pA)Wey^lZDi3(lq%){C-q*oC7tz$1a50npUn(F`{6p5@0)pI8t~ul8i?%64Y( znMG&``rAlXfo#OF-f$4tV_@J&{C5h)?k;eu`Oa7iNynwqW1+V6`bWs!#wX^PwCetB ziz2N?Dx=vqTQ`MwcTwwis;+D<(>3Yf!=~=8X47O~|P+=8Rg zd5CCD`k?O{vqhVBwZ96;B<45kGpy?CXc-X0Xa$=n>G;^*EG~vj>LJsXUF@?c1sa5P zzqZgN1?uapIfNW?aU-5mR}g(^Cn1w%h}+WVxv~xZ4cDr}JwNeed(z0lBc??WIzzud zvGXCa>K62rPx6X|`Kt5=JE+tdlq78>M)Sp|!mG<|oY1{p*vk%{|BZv*q#G|#U9H#B z@KK^ENQ#d1q|`si`&$z2so3r=IvLG}iSEpy9Sf-{K%Uq*LQDNK)y4MNF#SiS0!n-} zpcAe))9En5^VnH-=&l%92&MiA5Sa6f%W=U4JIr`j+b7Li*7jjq;!|DfM{=$8N+OLRh#E8t%YW zb!_3>r^&d&?wP2GI)HLwp^&BVBWf!3Q^HK&27)&_?~W|}guw5017KIa{Lx(-tH`}{`YpbLGwaML$TdI|M z#F0k^&`8Ads7pFdlKlu#8yk)JL8Y^_00YHTtLunn#Dkr5eILe)*|tF!(kG*hA0^)a zn+n&^)uNhhPZ7dN*G~&wHc>~^$4$Gn`ta^GB!6@uL1bm019$A1gjy(d3(K7LrAplZ z2sVw$7qQ`{`xr1`C_Q8mQh$tktRG@Kl=_6pArhz)j<$4-P_~EcgmLVa?jlVP6*tGU z-@U``ok)_y4<@qp>y!0z<(#@0xps7etG5((`)EDSx#B@JAr~#_bE3?cV}@Fc-lM>t ztkL%FZ82P`yF&Nh9#MW!5YlFan&m6H+4tr2>0?1&5-kcbjnO7K|PpQhyV0*sCRRFp#b_5u84lrH?K3 zKO>11nu4J$`J_DTgFv zED^^*c6S^0euQ-$fy5b%F}3?u(_@ack5suU;uA!Zcn6e zeq?tS=GGvOKM3sZ!i9Y;xbD(6N?UMcDeYVTWB@Gie7$-aRC5yrtX(PW>8AVQ)yY6( zEP*4m7=xHBbu6zwDG%L=Eit}KAcPY`sHWoncc){C-Vi(;d3~IZ--KJ6sPQ#tL$7a9 z*E(`_Xp2l$kOxe(@HOHXMr{%uh|xE-*0)(lG`es@`ZFkFx-q!nB(o_p~&JHSs{LWynva^lhN} z#~*N3C6(yz#nCMsh*3A-3}a`K?lZV~)6U)78|$~#UA4AK!@NfE_0H&cr@A)#JsiX6 z4;R+9(dD;z878Ke;Vx4?dJ3N7&{Y~C4LxHc|VZG3b-yS!=+G^(3X-=KiClv22=P&$d=I1#F|M4v= zjop@9s%Ayx;*p5HFbWnC*W}Z6K)7~Fp_Xx ze3K5h6|3A^X_vTd`TO_9a2?X}XtD*#RV%I4cSLZDBW@)^s}cCH?ylPeqnxsLth7uv zSGpsjuZz+B5%Xe2EmAeP%m`Q8;I>Np)-t~G5)&#NatF{ZKEkeh4SOJ0D0NzVI0u*2 z;QlGPn<)};oYslxR&lS@sty|P>)V8IG$DRNX{<3lJ4dLy7>79In zI)_ujx{F{?wtHDgg^;>s85c-*bEj|zpoPeRLNlt{sjp?iw?wSc+K6j4NI&qH?Wr`% zSHsc^xHajbW{v42u&orQ}aApDUCTqC+u$$lRfR!1ThVn_bjCFZSRBBTvL zB`%Q!hoZuKA_;ETCA7g)P_H%JAx03CQhqH#rABtocZjM08F zjhnu4dstNK2_IJjUHNiDsALoeH$f5{ckKXKwsa??$tZ3X+ryVT)A?$CO&UynG;9R$ z7F9t@Jr5&a~XHq*3nIk&d=82e| zW0;H{J1P*hr6&Ym+b1@hSrf*WD}rX|RwSZJ7&MJvF)4#WI0gv#XU@86ROzgsh1dnG z=Tpgam%bSpG1GBo5eFu<+1B!*Z?^>ZwMopLj?cBK*_L2ga~f`?8{ndHM%e^ye8(>~VkO;~PomJPdy8Y0bT1NL z*~q0lDaB;ea4p!!JoHdG^Xe5&D7#Ityo$dnp_5Fa5^eC<~-RC58ly98oYnTX# zo8WTCn&B0t+%j}h6he1Y8&*=vm24p53EX>29g{1eS?jc~gMPKjDx_A{Du9a3|Gf!fO1+tyWz!? zx_#_~;lG2D8aVQ(`lOt}Gvq3FMBWd(${{r8KQ}zdK45g|EP4_mQ~qT?-OQDHDD z#CEg8!4H(MFV9{{zlVN}MH^XIFZSEe=ZD1WiVR^q&?0LLGzc+rVTJi;)XTn&B{+YqDO;nc#;wtgCse(qd^b|slaB;(|h8TIW z*waYk?dc;}f}u4tMTbzoq)HNNnP>VQbz!s7pd1y<>4Q@ZlN210z90-CJ)_>Ro^VGQ zx9k7H z5m)?EE#<6Py4;|Y8+Vn%IV+CCN>=OC(4s{0&GY%DLNETI6dGkXNk-hm{I0gumDVlv zYl2fmFl<9%ki=4WPVaG33?B^0HJEI4a^hvqfrYp9;DUqv_Abn4Qjp3~o;?uIKOQJH zFl$b8tezMd7$RBnIG;6!^D%BjBXJQ9Q^0~`ri2}q&A1^7+F=$UqfC_F%z+h3RLMu-AkaKrn~QqiL+Zun9WH z+%a1%cm)b2r?5ZlaQ^4a9TUx%Ub*vI5xR@n)d!v$;1J);n$wCF{w1-kizXXAX?lG~5*Yvg02zoCqgU{2nv3#lr|tN1;&YF!cTr$ghFZqlx z``v2hm!C9&DDFa(=}gfalIw==n~;OOIhOdz(8C znVko*=Qg=D?|BgJ!8zwQvvALAZj;+2nUjfwUuJ%jJDL-(<&eXkvgNkkfq72+W&rJk z)AD2UnT?dakA>ULv2SMnFuNwqRc#C6kRZ?p%doKG67S~hkP!(&9?1~Rbhu{Gx#{eV znXEyebz41ePOyn)5Cs@T483id>AA?M<&4CPX^-Ku0|E(}w5jKXXcR`vlhnd_2?g7- zwm9PB8+MLRP{ z3IxI7JakFyB%tLI_cV$gD$vTW-T^{-=&VaF#%@_$OcLA8AT6w?Q^z~Jy~Knj9^s3O za(^wnF^c36(rwO_Fdm=juGxDE$ZbcpRPyMEXB_wkz_cjaTF3;|Owj=ctC##V6YUa% zTLWd5vVdoyEM18mYFNpa9TK7**n0XH0p@HXb#uw??{v98n|9vecq;a`QGvgN=I zKi|gQ0}pa)`iOF71z(#dOf@@p4md@LnUIxhksPhBdYc$=NXOn0EtfI_Lit49Rc8N* zMR&u~-LVoi*wxtD@a^hm0W6nv9oT(NaZ;i?y|>NM^DOwGVay1J?H|oRbciu@&Ojf# zCBS;;;6SPpR*{w52Qq&^6h9NLH<;vd7^WbGxJ1de(bmhyr647$=#lLZjy2}GVjzUsd;bGw(~64 zWBTg>$G|&up5=~8IaQcD-(}#K^w@cp8&WIy_sqTO+A?|8`T_mrZ|W{nlN-say2H^&&p!t@&b#*UUY?a)a~Eubiqe zE6KVA+{dK#wK%tag+aiTR*mU$k!Pgd4|D68;+6~VD)ZvMC_S_-!2LS%Zkf<h?1=;)joaF;|akMz#Kph<`tqJQzP{$Ya_aewXf-yIoVxOJ0T| zy8SX!P*Q8?ouQMfnX?~V^3wOW8jnFq&#v&!<_x|={-(slZF_U$viO+hM~%cC@z3Yl z_2U`+eq7>?BR9L`>F@RXBO-4TPv*k!dqux@%bSracYI~28<+U>j4O_dNIxPE=C)Ha zuG=y7z2t8qqRUmAf|Axe_CG`Z{=4IiGn`;k3Bbpyx{Hl=sMu%MdI&x*M{<>bjV~;+U{hwGhH6ArC z42GBMa>L)y>A1`XsP9Z&-&Os;?y*YaGbr}EWMeMB3?g3qGVe`lk3UU@q!Aep9yfGw zy*H0#GJdGJCD+dkelM49#$R~Zi~L*lIMdjbpp55DJ5AEgXj^VOk$=_gc=dN8s^7c) zgL;SJa)YmD>XOg@ZvPN};+?Up6XMrLKbb3^!_R4c1n2g+wJO zS0VE!_ei_A89(<=%2_+K{I{jwy4@0)*Yhf8`a2}_EBV0G@JxS?nt8(my4=iM?CgFh zq08U{y4+lka)+ec(R*~cIUePD%skVFbh%QGa#e!gh|sy%qg=kUJMlpc&&^-Mk80uD zkv?6{3tp4hqo#wpoR=IZf#+4u@FOJs?RikcbIZHIFDUSa_Um#1kNzGNxjH8O@ZwvU z^rPa#Zw;?Z^kYcidG&W#=AlPL4)(h6GjgRwp8KS~vs~p&f8)}R9_g=FyG_F1>igfC zesSr?sPL`OqupwetBABa+oRlZ;aiRL*9+buiFc1lf4$08h+hr$>3(>%J1BG+?bqeJ z;FU?cC8BR$a&XA#o8adqS5?yP*uJ->b4c19yHA&!=Rv<=Bd1c%OAZEwAGhDF;kofM zay2USJ0f<&4bPOT7I_(wcD?8_B>iYP2VH`K-%+tcUiw=m_POF5_Hb)g`p9h_d{`Ls{Y7Xgsc)@ED`I`_sg=bo<_Jw+C3xw(j9ji{EiAg@H_Cl-s`1r5u+dC zR~NeIV(2$4<%Yz6xb2JSZ=1;9L7|J69K?kmC&g~K%Ncmbguln`*ZuI)b4%zvF8bzW zpT|WnOQf7ze@(kJ(r%UT!wt`rGwYGV0?$kSYJ|Uq;xE1WTPAksIicSS4}OG%A8iu< zx%pxEHZJhiieK>3-#($sJkd)p{6eCaqap`h@XiQ7szk25?6{JCob;fJ=|_*?cSz*f z3*K=lH!S+*rk~;4pzwE8=;F42rkq)4>Jff;`L~dXb3~rK=rShykrDm%>PJNMHzM%d zdT!`aBK$of@s*doAC-0|gnnLnG$j0ZL+Iz01Jmvy;ct)lEiXGB5j|JJUoZOgNgNgu zx$@GZjOfuJY1gaVpx_r0zFpvUJKQf|6We;e( z=9W`K=L&&0_#s`+EvKejweam#iR--JwMjpYNI5V54w~_YM>(S($Ax~+iJW@1J8a@B zf#(&!42!(vpCjIlOS?7V_r2urh}Z+a*dZ^yjLZCiS2@$)punpUzu?wi<9~y~w-Jdu zz2rqnxg&y~7k|q{UfQI;UjFEa@GT>H?gg(z;CYoZ{k6nSW<)>S?Hc(jk#?1|Tk4^2 z`O38(#hBlXfS>&v^BtP53r0{=-W@#--ic#jbhr zBO~%R?4g%7ou%BM(9iPd?-{WN6+%BRdv!?o;Z@GaRYdw568zlu%HUTm?Z!pVz34nD z3N07OWA`w7yIYsKQhvfu|7>dFMl*D@Ray1ukmV+ z@OMJ&pO^mT8^0=Y;Ew-{oDK>7GJ>C%|2QP=wg~;a^s-9&`>>RA>ziq}P3U}3l6HjMc=&SIVg5xNaV#!&xb``%7o7Dat6OMVh@gpJbUq134V*DAMQBE zz#A5LJz`(n{4nK)#O@vvzwaI|m~xir`B9;>SG#d(_mGtHY*+X(A^PFf?irD*Vn`UL zJKQ7oASLqb6~{(Io~uMZy!7ap@b`?+*^3_;qaPv%Zv8d=7!$hm30=JC+#~k6QRLKZ zuME6VY447C5`!HB*Imx=?U?Z6n8dqYa@8mOjfg+;qVtfz8x#E8 zbTR!c5qYi>c(5xts!LLc|n%6wnnDnBlWFZ`5=+XbFiyJw^yw~Kvt_rvfvU;NgmrN3^uGUW!PT$S+GOW!8Uct+rP z^*1Q}s1W?T^eA7-eMH)I<7fD02|v8bnSPW=xxqfoU$=cR<@$sljbewq+Kotm2PNKc z%e{@C$aA0A4Y%Bza>vCVWu#p%`b`))khsLF-7=AbSI;2_N5%h+epu7RZJ!N(O@iMs z4?Awk9W&!2k8(DhjT{L5yzI~sq2EF2uis101;25T7cV;Z2!58>4KMnQ2t2QHrXSTJ zS7j1!xb?%7s}MSuh#m48H}(nNdPI-B;@xVA54_5mc1uKmtA&1E@#%5l$B5YHv)k2# z9~1hW5&pXSYvjNZ``01-aL3uE+=Sq_Nc7`ud!^g$Gx8^P(u;3xLYKJsb1(cxMSshs zzixYA@GBE|UgZp(m6TJ$UoSm6F8;UTT>ePxkXJd=?vT)LLg?pZug(a6uNS^e^U$L{ z6X%@E9teK-h+KKKTP5;x^c?MgII!&dYx&X?IxY>}4lQ zjK7rl-mAaI1-~(w-*EdkBY#!Gx1jXbOAd|*etlBT%l?&!9kK*JFL4f0f9=*n>LW@T7~ht3;2y`dcOX_NtVd=3&<+L=JjnT;Zv| z(vLx*i&we0vAaU&4X$=I^^`gycCAMG>!oi;#SWE-9(n1<*g47>xj!cOSu*eArk}yD zM)dcn*n2PjHc7iDh0b362nn6N${GB6q}>|vgI@3~qaPl0G4O)Ix0LunFL=kqZ&}h` zFTXV^dNd~OddX>-$Z1^o>-PHwzZ&sJkq>FPcgLruT$RYn@pH@{L)L zfJp@yET~D9fz(Q4K!6Lv*r4)MsFNtg5Rq{j5x@w>4+XnaZ6VckoFY0fh)06}14r+p zx0;%w@UmK55DaYq}@*?X;Z z&b^+y&-A>V>ALMJKcchy^gjB{=EXI=-o@`xH^S4n@=WW-zWzUSGP_TIFIrW_H)+k z=cw8LBP|$}%sxMRZeRHQ@icE19&cfP*Lr*XyR~=jld$l3v-8cyIUD!Fan6oEJMZi` z@h{rv@gDcizwbAmvD?ir+l;TU<8FTX!Q_*6yYUs9(s<|J|GR%^x3_2WH?2Rj^Uk=h z_z!>8{=9$IaY|$Nhb#UU4DbK#OZWRU53~Pon8nd7u4ZwwFs^3tH@onCt!FT5ucv3$ zamw+|5660b(EE$U-=}%l*LtL<_ZRPi_3-ZcaI8o7gBr(lK8!~`pm|uZ9v`|d`31m< zXn2!T*o#zF@<2So2jTA_XWD7;%ZIb4I$oJT0le~7v=(o`w~r86k7v(ZsUIn_5#NSK z9m~&$ct7O6Wc5*Ac>?`tkHaE*3a_l7Azpdr$s&94%Bzs)W~arQ@GJ4k1`6YqPofxJ z`3AZMuiXC(ksI*r%OnT#u(wk^JQ4Nr$|Eiic@eKX7cIWUDPefsh4z@)lTX?gi7caC z`4n1>SN2f|uS{>CKVJDLO5&Acl))=Mb~)FMSNhOHc;yH^g_oGfMSsDyJEAM=a_{1ykLs7i)X|xTmJmYbZ?f59X z{z=Y@XOA^`;2%YHvaan9m{CMSgXdPa;8(oTLpDuZGBVTh}vT084xMxmMw6o8!RE#-m z9ouk#>a;7buFXjouY3UQ!Sfs|-4=Xf0Q6g~=H{&j~uiyuQ-=8y?q z`S}$Nao*vyxB>6S^UN#1;dfXy#|yuJg0w5Ms~oZpuY3eugIE3l-G)~#KGh*6o;|Fs4_pp7({6Xe@114058=({IAkxM zHQ*Cy@tsbKuYSfM9=tMm9>Obs{=0U6S?iGU=WO4Go5Hq_!=>lj-V2{wXZs;MKH`v= z9tXa0k=-s|a>%{w?REq1Lfh%1+*M{R)Weri7e9vkn_LTC`Qtj*f>*wQoOiJn4dxOp z!6)JMf8~&6c=m0V%XixA&a;yI1nE79=STV4eGXYoANGcoXHW>w{))2sZ|pwoIVqpK z-|oXcZE`Qt`wn}$%HbWm5BuCo7U^-=Yg1Nz+iqw7QF$F{yFB2KKSS2N51##?-OkUz z@&dBP1FwF__UyGRpMQAT2Yv_XdD-(=?taAX!*hmwe3$Kc_L2vYo{Q%nIkIc_XRmRo zB0WC)ughhBYxiN#H+d6jJNw|teaQMOJnvDvo&CFI(Qdn)pBdzvNc;11lw9!lc6$^a z`k3v#@Rc7qB+MAb@ctL={$2Q~k=-Wd~aSJ z!YkLKK0XOwKsx3zbbo(dhIr*tv=^`ZIFcPsix=US;FWhEKVI2DXW^BPqjh-Y^XO9i z7@qp zw~+Nb^TND5fJU?{pGSM~%4I*A7uVhNhc_UN)if-;Xn(d1Px-O!4ftKOjQ+~!kPol? z#7m4HuRIBb@yhd26d#3EWW@};?q%DjVSh9)ar!7BI?nuymAj?#VfBxd+^Gqknk+uOg!aSn1yiZLrM zL>as?hU$3UlOrW$T?_0U?39OSSMGNR&-i%d5$HvH6yCLj`J)c-P8_-FP}?VA5n0y@ z-$Z&$|6x1}E_GV>cR#!qX}j{DwVh)s5Arz0l_j>}(@38shA?!5{n;@57P8g_K94-~ zSH6ml#w$b1=z~{oLlM0478Ju9@Sr1k9>;s(O0*rXJQdx5S6=U>FJ75PJ-l)k8sNL| z6Gu5^g!jW8$jWhe$kB|McI912ayB2wFTwZWxu3NAgyD(5$@pnkUWZoVl~okNx8cW@ zb1it~EhvUpehXcLXTM;%*k{je5C>C+qouRQ-4 zt`Q%FFQGA>eT(JKkF|3x4Zm@meZB0fEPIizsra387qKD_dPXmJ@jQ+|Vx)lGx{o`jiPL7iA90Nb+(!lTT-_=g3 zmk{5J8jIxScYetFYW3-fO&h&e^RnbCwrpOr{P<)2%Xu;QCA<@G^W|%nU$E|+V*<;) zTetDHz>7C-*?ifW<$rY9*5#+4cHsW2c?scVo4(2mzB$0=t!tM5!Bv}A+3yY9dd#LP zF1c#U)-4;h9dpT+O{*^6y6M=fk6-THbn)gZHe9xK+lBx8Y83ET zuM<#mmE0vy$y@T5Ql)fB;?zl+{1&*w9={c zD#MDa>Z|&z-BzzPXboGV)}+NZ`_w!jhRg66eluW7#Z_@vJQZKXUkOx#m2f3eiB)7s zPSLGh?NqzfL3LOiRi);txof_fzZR&4Ymr*CmZ&AIakOi_TE8}^jcb#dr|zu>8CR?x zuczyodZ*s459_1)xbAAW8=i*05oiP(kw&x;YxEk!hO6ms#+vbFrfD?IX15t^g<9cO zq!n$&TJcu0m1<>LMyuWGw7S-HFsL!PuD^UYyoS#R8c`!=#EpcJG*U*!FpRd*F}lXU z7#d?^Vo27V^<=$Se>RW}X2aP?HkwUljjWk%XFJ(ZR&uGFD<93r@`-#hpUE5fc7Bi_ z=M#lVAzX|Uqs3S;UQ85|MYGr~hDza5ujDQJ%E5A|94SZ3v2wheC@0IQa;9vQ2jy`& zVJ6LqY0qQ4lBlFA=}M+zR@&CO^eY3_W>k@?tLmwGS)oWZUY%4E|1*O==C4-|HH?PY z=rjh6aU)^PO}p7?+H;e!=0jI?tZOQZByG)y%bF2Ca}qKl%t+esWHZ@uc9M1F+&NFq zp9|z7xo9rQHJiD1uAdv^COIA=^1gg1AI`^_1D%0xzLy^{2kwHW5GVu-(L$_{Dx?eT zLZ>h&3=2|p6@5j2QRhcz#$axG#eQ*IoD?-~gQZX@R*Dn5nNp|JEe%VflB?`4`^y1q z&UCiSa=YATz9wZcy{69$nPD?-Cd`a!m|e4Hj?A&Caj7vGB_eeebq00zbnY~^!^~Zx znyeaCv)Zfnt7GQTQ}fn>wNNcqi`UY%Os!Mv)`qoF%~f~T{q=yAEy;SSZr0oNetl4% z)TQBV_!^-`xDjt88X4xjOJt6icX!j%3^aqyC=r@!rklC~gXXX)EmzCe@>_A5AV!Ur zNtX3l4J*goi-cd*iHFtDmC)!+6PX>Na%hYU7ZIt^Xh&koio||)z$!`3oAc#Dxo|F? zOXM;+BiGIKa--Zh=gxcbfqc-4(^Ni9jCP39VO|QZg0J8&gbR^EqL3^Y1+&mA^b2F6 z*HiQsgT;^)zv*J8*dcz0#ZghKi@y{oMM}|9k_uy%+NFMJP@0satdSimhpm{_ca zxNd1R3qJJTbVIz zNYmByQ9;7ZNHft)T6xlI_M7A8r0HpSTUt$G)R8n*L@S8aLGdn<0_)>4+=kx>kSS5C zc9>L-K6OKDhBxcWhNu?tY=R6jvfXTtS~1RQ76o!aawtX)rOBZVIW#1PT;!0S9EwXAd^n!D~Hhl17}Cq;d4 zQ{M;FcWJoDAwM}3A%~Jy?dmo9e&L3T(ExyT_uITRs> zlH`y{4)w{QNzOy13sUJ~RJt^ku0y38Qt4a;H#rm_hoa(U5ZNArqT_lbW(PaLw<57V%--EDqW9CH>T2gsB}RpU5rYX zrqXq&bVDkgi#xENN*AHhC8=~Km99^vn^5VzRJstAE>5M(Q0cl4sD~SJO=n1<0W& zIh5i)+$M(xH)YMeW{6fXH}fC1?k*8#cvy@P@0{>g=-+<>H|2rX literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/schema.py b/libs/win/pydantic/schema.py new file mode 100644 index 00000000..e7af56f1 --- /dev/null +++ b/libs/win/pydantic/schema.py @@ -0,0 +1,1153 @@ +import re +import warnings +from collections import defaultdict +from datetime import date, datetime, time, timedelta +from decimal import Decimal +from enum import Enum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + ForwardRef, + FrozenSet, + Generic, + Iterable, + List, + Optional, + Pattern, + Sequence, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) +from uuid import UUID + +from typing_extensions import Annotated, Literal + +from .fields import ( + MAPPING_LIKE_SHAPES, + SHAPE_DEQUE, + SHAPE_FROZENSET, + SHAPE_GENERIC, + SHAPE_ITERABLE, + SHAPE_LIST, + SHAPE_SEQUENCE, + SHAPE_SET, + SHAPE_SINGLETON, + SHAPE_TUPLE, + SHAPE_TUPLE_ELLIPSIS, + FieldInfo, + ModelField, +) +from .json import pydantic_encoder +from .networks import AnyUrl, EmailStr +from .types import ( + ConstrainedDecimal, + ConstrainedFloat, + ConstrainedFrozenSet, + ConstrainedInt, + ConstrainedList, + ConstrainedSet, + SecretBytes, + SecretStr, + StrictBytes, + StrictStr, + conbytes, + condecimal, + confloat, + confrozenset, + conint, + conlist, + conset, + constr, +) +from .typing import ( + all_literal_values, + get_args, + get_origin, + get_sub_types, + is_callable_type, + is_literal_type, + is_namedtuple, + is_none_type, + is_union, +) +from .utils import ROOT_KEY, get_model, lenient_issubclass + +if TYPE_CHECKING: + from .dataclasses import Dataclass + from .main import BaseModel + +default_prefix = '#/definitions/' +default_ref_template = '#/definitions/{model}' + +TypeModelOrEnum = Union[Type['BaseModel'], Type[Enum]] +TypeModelSet = Set[TypeModelOrEnum] + + +def _apply_modify_schema( + modify_schema: Callable[..., None], field: Optional[ModelField], field_schema: Dict[str, Any] +) -> None: + from inspect import signature + + sig = signature(modify_schema) + args = set(sig.parameters.keys()) + if 'field' in args or 'kwargs' in args: + modify_schema(field_schema, field=field) + else: + modify_schema(field_schema) + + +def schema( + models: Sequence[Union[Type['BaseModel'], Type['Dataclass']]], + *, + by_alias: bool = True, + title: Optional[str] = None, + description: Optional[str] = None, + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, +) -> Dict[str, Any]: + """ + Process a list of models and generate a single JSON Schema with all of them defined in the ``definitions`` + top-level JSON key, including their sub-models. + + :param models: a list of models to include in the generated JSON Schema + :param by_alias: generate the schemas using the aliases defined, if any + :param title: title for the generated schema that includes the definitions + :param description: description for the generated schema + :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the + default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere + else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the + top-level key ``definitions``, so you can extract them from there. But all the references will have the set + prefix. + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful + for references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For + a sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :return: dict with the JSON Schema with a ``definitions`` top-level key including the schema definitions for + the models and sub-models passed in ``models``. + """ + clean_models = [get_model(model) for model in models] + flat_models = get_flat_models_from_models(clean_models) + model_name_map = get_model_name_map(flat_models) + definitions = {} + output_schema: Dict[str, Any] = {} + if title: + output_schema['title'] = title + if description: + output_schema['description'] = description + for model in clean_models: + m_schema, m_definitions, m_nested_models = model_process_schema( + model, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + ) + definitions.update(m_definitions) + model_name = model_name_map[model] + definitions[model_name] = m_schema + if definitions: + output_schema['definitions'] = definitions + return output_schema + + +def model_schema( + model: Union[Type['BaseModel'], Type['Dataclass']], + by_alias: bool = True, + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, +) -> Dict[str, Any]: + """ + Generate a JSON Schema for one model. With all the sub-models defined in the ``definitions`` top-level + JSON key. + + :param model: a Pydantic model (a class that inherits from BaseModel) + :param by_alias: generate the schemas using the aliases defined, if any + :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the + default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere + else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the + top-level key ``definitions``, so you can extract them from there. But all the references will have the set + prefix. + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for + references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a + sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :return: dict with the JSON Schema for the passed ``model`` + """ + model = get_model(model) + flat_models = get_flat_models_from_model(model) + model_name_map = get_model_name_map(flat_models) + model_name = model_name_map[model] + m_schema, m_definitions, nested_models = model_process_schema( + model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix, ref_template=ref_template + ) + if model_name in nested_models: + # model_name is in Nested models, it has circular references + m_definitions[model_name] = m_schema + m_schema = get_schema_ref(model_name, ref_prefix, ref_template, False) + if m_definitions: + m_schema.update({'definitions': m_definitions}) + return m_schema + + +def get_field_info_schema(field: ModelField, schema_overrides: bool = False) -> Tuple[Dict[str, Any], bool]: + + # If no title is explicitly set, we don't set title in the schema for enums. + # The behaviour is the same as `BaseModel` reference, where the default title + # is in the definitions part of the schema. + schema_: Dict[str, Any] = {} + if field.field_info.title or not lenient_issubclass(field.type_, Enum): + schema_['title'] = field.field_info.title or field.alias.title().replace('_', ' ') + + if field.field_info.title: + schema_overrides = True + + if field.field_info.description: + schema_['description'] = field.field_info.description + schema_overrides = True + + if not field.required and field.default is not None and not is_callable_type(field.outer_type_): + schema_['default'] = encode_default(field.default) + schema_overrides = True + + return schema_, schema_overrides + + +def field_schema( + field: ModelField, + *, + by_alias: bool = True, + model_name_map: Dict[TypeModelOrEnum, str], + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, + known_models: TypeModelSet = None, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Process a Pydantic field and return a tuple with a JSON Schema for it as the first item. + Also return a dictionary of definitions with models as keys and their schemas as values. If the passed field + is a model and has sub-models, and those sub-models don't have overrides (as ``title``, ``default``, etc), they + will be included in the definitions and referenced in the schema instead of included recursively. + + :param field: a Pydantic ``ModelField`` + :param by_alias: use the defined alias (if any) in the returned schema + :param model_name_map: used to generate the JSON Schema references to other models included in the definitions + :param ref_prefix: the JSON Pointer prefix to use for references to other schemas, if None, the default of + #/definitions/ will be used + :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for + references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a + sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``. + :param known_models: used to solve circular references + :return: tuple of the schema for this field and additional definitions + """ + s, schema_overrides = get_field_info_schema(field) + + validation_schema = get_field_schema_validations(field) + if validation_schema: + s.update(validation_schema) + schema_overrides = True + + f_schema, f_definitions, f_nested_models = field_type_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models or set(), + ) + + # $ref will only be returned when there are no schema_overrides + if '$ref' in f_schema: + return f_schema, f_definitions, f_nested_models + else: + s.update(f_schema) + return s, f_definitions, f_nested_models + + +numeric_types = (int, float, Decimal) +_str_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = ( + ('max_length', numeric_types, 'maxLength'), + ('min_length', numeric_types, 'minLength'), + ('regex', str, 'pattern'), +) + +_numeric_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = ( + ('gt', numeric_types, 'exclusiveMinimum'), + ('lt', numeric_types, 'exclusiveMaximum'), + ('ge', numeric_types, 'minimum'), + ('le', numeric_types, 'maximum'), + ('multiple_of', numeric_types, 'multipleOf'), +) + + +def get_field_schema_validations(field: ModelField) -> Dict[str, Any]: + """ + Get the JSON Schema validation keywords for a ``field`` with an annotation of + a Pydantic ``FieldInfo`` with validation arguments. + """ + f_schema: Dict[str, Any] = {} + + if lenient_issubclass(field.type_, Enum): + # schema is already updated by `enum_process_schema`; just update with field extra + if field.field_info.extra: + f_schema.update(field.field_info.extra) + return f_schema + + if lenient_issubclass(field.type_, (str, bytes)): + for attr_name, t, keyword in _str_types_attrs: + attr = getattr(field.field_info, attr_name, None) + if isinstance(attr, t): + f_schema[keyword] = attr + if lenient_issubclass(field.type_, numeric_types) and not issubclass(field.type_, bool): + for attr_name, t, keyword in _numeric_types_attrs: + attr = getattr(field.field_info, attr_name, None) + if isinstance(attr, t): + f_schema[keyword] = attr + if field.field_info is not None and field.field_info.const: + f_schema['const'] = field.default + if field.field_info.extra: + f_schema.update(field.field_info.extra) + modify_schema = getattr(field.outer_type_, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + return f_schema + + +def get_model_name_map(unique_models: TypeModelSet) -> Dict[TypeModelOrEnum, str]: + """ + Process a set of models and generate unique names for them to be used as keys in the JSON Schema + definitions. By default the names are the same as the class name. But if two models in different Python + modules have the same name (e.g. "users.Model" and "items.Model"), the generated names will be + based on the Python module path for those conflicting models to prevent name collisions. + + :param unique_models: a Python set of models + :return: dict mapping models to names + """ + name_model_map = {} + conflicting_names: Set[str] = set() + for model in unique_models: + model_name = normalize_name(model.__name__) + if model_name in conflicting_names: + model_name = get_long_model_name(model) + name_model_map[model_name] = model + elif model_name in name_model_map: + conflicting_names.add(model_name) + conflicting_model = name_model_map.pop(model_name) + name_model_map[get_long_model_name(conflicting_model)] = conflicting_model + name_model_map[get_long_model_name(model)] = model + else: + name_model_map[model_name] = model + return {v: k for k, v in name_model_map.items()} + + +def get_flat_models_from_model(model: Type['BaseModel'], known_models: TypeModelSet = None) -> TypeModelSet: + """ + Take a single ``model`` and generate a set with itself and all the sub-models in the tree. I.e. if you pass + model ``Foo`` (subclass of Pydantic ``BaseModel``) as ``model``, and it has a field of type ``Bar`` (also + subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also subclass of ``BaseModel``), + the return value will be ``set([Foo, Bar, Baz])``. + + :param model: a Pydantic ``BaseModel`` subclass + :param known_models: used to solve circular references + :return: a set with the initial model and all its sub-models + """ + known_models = known_models or set() + flat_models: TypeModelSet = set() + flat_models.add(model) + known_models |= flat_models + fields = cast(Sequence[ModelField], model.__fields__.values()) + flat_models |= get_flat_models_from_fields(fields, known_models=known_models) + return flat_models + + +def get_flat_models_from_field(field: ModelField, known_models: TypeModelSet) -> TypeModelSet: + """ + Take a single Pydantic ``ModelField`` (from a model) that could have been declared as a sublcass of BaseModel + (so, it could be a submodel), and generate a set with its model and all the sub-models in the tree. + I.e. if you pass a field that was declared to be of type ``Foo`` (subclass of BaseModel) as ``field``, and that + model ``Foo`` has a field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of + type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + + :param field: a Pydantic ``ModelField`` + :param known_models: used to solve circular references + :return: a set with the model used in the declaration for this field, if any, and all its sub-models + """ + from .main import BaseModel + + flat_models: TypeModelSet = set() + + field_type = field.type_ + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): + field_type = field_type.__pydantic_model__ + + if field.sub_fields and not lenient_issubclass(field_type, BaseModel): + flat_models |= get_flat_models_from_fields(field.sub_fields, known_models=known_models) + elif lenient_issubclass(field_type, BaseModel) and field_type not in known_models: + flat_models |= get_flat_models_from_model(field_type, known_models=known_models) + elif lenient_issubclass(field_type, Enum): + flat_models.add(field_type) + return flat_models + + +def get_flat_models_from_fields(fields: Sequence[ModelField], known_models: TypeModelSet) -> TypeModelSet: + """ + Take a list of Pydantic ``ModelField``s (from a model) that could have been declared as subclasses of ``BaseModel`` + (so, any of them could be a submodel), and generate a set with their models and all the sub-models in the tree. + I.e. if you pass a the fields of a model ``Foo`` (subclass of ``BaseModel``) as ``fields``, and on of them has a + field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also + subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + + :param fields: a list of Pydantic ``ModelField``s + :param known_models: used to solve circular references + :return: a set with any model declared in the fields, and all their sub-models + """ + flat_models: TypeModelSet = set() + for field in fields: + flat_models |= get_flat_models_from_field(field, known_models=known_models) + return flat_models + + +def get_flat_models_from_models(models: Sequence[Type['BaseModel']]) -> TypeModelSet: + """ + Take a list of ``models`` and generate a set with them and all their sub-models in their trees. I.e. if you pass + a list of two models, ``Foo`` and ``Bar``, both subclasses of Pydantic ``BaseModel`` as models, and ``Bar`` has + a field of type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``. + """ + flat_models: TypeModelSet = set() + for model in models: + flat_models |= get_flat_models_from_model(model) + return flat_models + + +def get_long_model_name(model: TypeModelOrEnum) -> str: + return f'{model.__module__}__{model.__qualname__}'.replace('.', '__') + + +def field_type_schema( + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Used by ``field_schema()``, you probably should be using that function. + + Take a single ``field`` and generate the schema for its type only, not including additional + information as title, etc. Also return additional schema definitions, from sub-models. + """ + from .main import BaseModel # noqa: F811 + + definitions = {} + nested_models: Set[str] = set() + f_schema: Dict[str, Any] + if field.shape in { + SHAPE_LIST, + SHAPE_TUPLE_ELLIPSIS, + SHAPE_SEQUENCE, + SHAPE_SET, + SHAPE_FROZENSET, + SHAPE_ITERABLE, + SHAPE_DEQUE, + }: + items_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + f_schema = {'type': 'array', 'items': items_schema} + if field.shape in {SHAPE_SET, SHAPE_FROZENSET}: + f_schema['uniqueItems'] = True + + elif field.shape in MAPPING_LIKE_SHAPES: + f_schema = {'type': 'object'} + key_field = cast(ModelField, field.key_field) + regex = getattr(key_field.type_, 'regex', None) + items_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + if regex: + # Dict keys have a regex pattern + # items_schema might be a schema or empty dict, add it either way + f_schema['patternProperties'] = {regex.pattern: items_schema} + elif items_schema: + # The dict values are not simply Any, so they need a schema + f_schema['additionalProperties'] = items_schema + elif field.shape == SHAPE_TUPLE or (field.shape == SHAPE_GENERIC and not issubclass(field.type_, BaseModel)): + sub_schema = [] + sub_fields = cast(List[ModelField], field.sub_fields) + for sf in sub_fields: + sf_schema, sf_definitions, sf_nested_models = field_type_schema( + sf, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(sf_definitions) + nested_models.update(sf_nested_models) + sub_schema.append(sf_schema) + + sub_fields_len = len(sub_fields) + if field.shape == SHAPE_GENERIC: + all_of_schemas = sub_schema[0] if sub_fields_len == 1 else {'type': 'array', 'items': sub_schema} + f_schema = {'allOf': [all_of_schemas]} + else: + f_schema = { + 'type': 'array', + 'minItems': sub_fields_len, + 'maxItems': sub_fields_len, + } + if sub_fields_len >= 1: + f_schema['items'] = sub_schema + else: + assert field.shape in {SHAPE_SINGLETON, SHAPE_GENERIC}, field.shape + f_schema, f_definitions, f_nested_models = field_singleton_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(f_definitions) + nested_models.update(f_nested_models) + + # check field type to avoid repeated calls to the same __modify_schema__ method + if field.type_ != field.outer_type_: + if field.shape == SHAPE_GENERIC: + field_type = field.type_ + else: + field_type = field.outer_type_ + modify_schema = getattr(field_type, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + return f_schema, definitions, nested_models + + +def model_process_schema( + model: TypeModelOrEnum, + *, + by_alias: bool = True, + model_name_map: Dict[TypeModelOrEnum, str], + ref_prefix: Optional[str] = None, + ref_template: str = default_ref_template, + known_models: TypeModelSet = None, + field: Optional[ModelField] = None, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + Used by ``model_schema()``, you probably should be using that function. + + Take a single ``model`` and generate its schema. Also return additional schema definitions, from sub-models. The + sub-models of the returned schema will be referenced, but their definitions will not be included in the schema. All + the definitions are returned as the second value. + """ + from inspect import getdoc, signature + + known_models = known_models or set() + if lenient_issubclass(model, Enum): + model = cast(Type[Enum], model) + s = enum_process_schema(model, field=field) + return s, {}, set() + model = cast(Type['BaseModel'], model) + s = {'title': model.__config__.title or model.__name__} + doc = getdoc(model) + if doc: + s['description'] = doc + known_models.add(model) + m_schema, m_definitions, nested_models = model_type_schema( + model, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + s.update(m_schema) + schema_extra = model.__config__.schema_extra + if callable(schema_extra): + if len(signature(schema_extra).parameters) == 1: + schema_extra(s) + else: + schema_extra(s, model) + else: + s.update(schema_extra) + return s, m_definitions, nested_models + + +def model_type_schema( + model: Type['BaseModel'], + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + You probably should be using ``model_schema()``, this function is indirectly used by that function. + + Take a single ``model`` and generate the schema for its type only, not including additional + information as title, etc. Also return additional schema definitions, from sub-models. + """ + properties = {} + required = [] + definitions: Dict[str, Any] = {} + nested_models: Set[str] = set() + for k, f in model.__fields__.items(): + try: + f_schema, f_definitions, f_nested_models = field_schema( + f, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + except SkipField as skip: + warnings.warn(skip.message, UserWarning) + continue + definitions.update(f_definitions) + nested_models.update(f_nested_models) + if by_alias: + properties[f.alias] = f_schema + if f.required: + required.append(f.alias) + else: + properties[k] = f_schema + if f.required: + required.append(k) + if ROOT_KEY in properties: + out_schema = properties[ROOT_KEY] + out_schema['title'] = model.__config__.title or model.__name__ + else: + out_schema = {'type': 'object', 'properties': properties} + if required: + out_schema['required'] = required + if model.__config__.extra == 'forbid': + out_schema['additionalProperties'] = False + return out_schema, definitions, nested_models + + +def enum_process_schema(enum: Type[Enum], *, field: Optional[ModelField] = None) -> Dict[str, Any]: + """ + Take a single `enum` and generate its schema. + + This is similar to the `model_process_schema` function, but applies to ``Enum`` objects. + """ + schema_: Dict[str, Any] = { + 'title': enum.__name__, + # Python assigns all enums a default docstring value of 'An enumeration', so + # all enums will have a description field even if not explicitly provided. + 'description': enum.__doc__ or 'An enumeration.', + # Add enum values and the enum field type to the schema. + 'enum': [item.value for item in cast(Iterable[Enum], enum)], + } + + add_field_type_to_schema(enum, schema_) + + modify_schema = getattr(enum, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, schema_) + + return schema_ + + +def field_singleton_sub_fields_schema( + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + This function is indirectly used by ``field_schema()``, you probably should be using that function. + + Take a list of Pydantic ``ModelField`` from the declaration of a type with parameters, and generate their + schema. I.e., fields used as "type parameters", like ``str`` and ``int`` in ``Tuple[str, int]``. + """ + sub_fields = cast(List[ModelField], field.sub_fields) + definitions = {} + nested_models: Set[str] = set() + if len(sub_fields) == 1: + return field_type_schema( + sub_fields[0], + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + else: + s: Dict[str, Any] = {} + # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminator-object + field_has_discriminator: bool = field.discriminator_key is not None + if field_has_discriminator: + assert field.sub_fields_mapping is not None + + discriminator_models_refs: Dict[str, Union[str, Dict[str, Any]]] = {} + + for discriminator_value, sub_field in field.sub_fields_mapping.items(): + # sub_field is either a `BaseModel` or directly an `Annotated` `Union` of many + if is_union(get_origin(sub_field.type_)): + sub_models = get_sub_types(sub_field.type_) + discriminator_models_refs[discriminator_value] = { + model_name_map[sub_model]: get_schema_ref( + model_name_map[sub_model], ref_prefix, ref_template, False + ) + for sub_model in sub_models + } + else: + sub_field_type = sub_field.type_ + if hasattr(sub_field_type, '__pydantic_model__'): + sub_field_type = sub_field_type.__pydantic_model__ + + discriminator_model_name = model_name_map[sub_field_type] + discriminator_model_ref = get_schema_ref(discriminator_model_name, ref_prefix, ref_template, False) + discriminator_models_refs[discriminator_value] = discriminator_model_ref['$ref'] + + s['discriminator'] = { + 'propertyName': field.discriminator_alias, + 'mapping': discriminator_models_refs, + } + + sub_field_schemas = [] + for sf in sub_fields: + sub_schema, sub_definitions, sub_nested_models = field_type_schema( + sf, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + definitions.update(sub_definitions) + if schema_overrides and 'allOf' in sub_schema: + # if the sub_field is a referenced schema we only need the referenced + # object. Otherwise we will end up with several allOf inside anyOf/oneOf. + # See https://github.com/pydantic/pydantic/issues/1209 + sub_schema = sub_schema['allOf'][0] + + if sub_schema.keys() == {'discriminator', 'oneOf'}: + # we don't want discriminator information inside oneOf choices, this is dealt with elsewhere + sub_schema.pop('discriminator') + sub_field_schemas.append(sub_schema) + nested_models.update(sub_nested_models) + s['oneOf' if field_has_discriminator else 'anyOf'] = sub_field_schemas + return s, definitions, nested_models + + +# Order is important, e.g. subclasses of str must go before str +# this is used only for standard library types, custom types should use __modify_schema__ instead +field_class_to_schema: Tuple[Tuple[Any, Dict[str, Any]], ...] = ( + (Path, {'type': 'string', 'format': 'path'}), + (datetime, {'type': 'string', 'format': 'date-time'}), + (date, {'type': 'string', 'format': 'date'}), + (time, {'type': 'string', 'format': 'time'}), + (timedelta, {'type': 'number', 'format': 'time-delta'}), + (IPv4Network, {'type': 'string', 'format': 'ipv4network'}), + (IPv6Network, {'type': 'string', 'format': 'ipv6network'}), + (IPv4Interface, {'type': 'string', 'format': 'ipv4interface'}), + (IPv6Interface, {'type': 'string', 'format': 'ipv6interface'}), + (IPv4Address, {'type': 'string', 'format': 'ipv4'}), + (IPv6Address, {'type': 'string', 'format': 'ipv6'}), + (Pattern, {'type': 'string', 'format': 'regex'}), + (str, {'type': 'string'}), + (bytes, {'type': 'string', 'format': 'binary'}), + (bool, {'type': 'boolean'}), + (int, {'type': 'integer'}), + (float, {'type': 'number'}), + (Decimal, {'type': 'number'}), + (UUID, {'type': 'string', 'format': 'uuid'}), + (dict, {'type': 'object'}), + (list, {'type': 'array', 'items': {}}), + (tuple, {'type': 'array', 'items': {}}), + (set, {'type': 'array', 'items': {}, 'uniqueItems': True}), + (frozenset, {'type': 'array', 'items': {}, 'uniqueItems': True}), +) + +json_scheme = {'type': 'string', 'format': 'json-string'} + + +def add_field_type_to_schema(field_type: Any, schema_: Dict[str, Any]) -> None: + """ + Update the given `schema` with the type-specific metadata for the given `field_type`. + + This function looks through `field_class_to_schema` for a class that matches the given `field_type`, + and then modifies the given `schema` with the information from that type. + """ + for type_, t_schema in field_class_to_schema: + # Fallback for `typing.Pattern` and `re.Pattern` as they are not a valid class + if lenient_issubclass(field_type, type_) or field_type is type_ is Pattern: + schema_.update(t_schema) + break + + +def get_schema_ref(name: str, ref_prefix: Optional[str], ref_template: str, schema_overrides: bool) -> Dict[str, Any]: + if ref_prefix: + schema_ref = {'$ref': ref_prefix + name} + else: + schema_ref = {'$ref': ref_template.format(model=name)} + return {'allOf': [schema_ref]} if schema_overrides else schema_ref + + +def field_singleton_schema( # noqa: C901 (ignore complexity) + field: ModelField, + *, + by_alias: bool, + model_name_map: Dict[TypeModelOrEnum, str], + ref_template: str, + schema_overrides: bool = False, + ref_prefix: Optional[str] = None, + known_models: TypeModelSet, +) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]: + """ + This function is indirectly used by ``field_schema()``, you should probably be using that function. + + Take a single Pydantic ``ModelField``, and return its schema and any additional definitions from sub-models. + """ + from .main import BaseModel + + definitions: Dict[str, Any] = {} + nested_models: Set[str] = set() + field_type = field.type_ + + # Recurse into this field if it contains sub_fields and is NOT a + # BaseModel OR that BaseModel is a const + if field.sub_fields and ( + (field.field_info and field.field_info.const) or not lenient_issubclass(field_type, BaseModel) + ): + return field_singleton_sub_fields_schema( + field, + by_alias=by_alias, + model_name_map=model_name_map, + schema_overrides=schema_overrides, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + if field_type is Any or field_type is object or field_type.__class__ == TypeVar or get_origin(field_type) is type: + return {}, definitions, nested_models # no restrictions + if is_none_type(field_type): + return {'type': 'null'}, definitions, nested_models + if is_callable_type(field_type): + raise SkipField(f'Callable {field.name} was excluded from schema since JSON schema has no equivalent type.') + f_schema: Dict[str, Any] = {} + if field.field_info is not None and field.field_info.const: + f_schema['const'] = field.default + + if is_literal_type(field_type): + values = all_literal_values(field_type) + + if len({v.__class__ for v in values}) > 1: + return field_schema( + multitypes_literal_field_for_schema(values, field), + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + ) + + # All values have the same type + field_type = values[0].__class__ + f_schema['enum'] = list(values) + add_field_type_to_schema(field_type, f_schema) + elif lenient_issubclass(field_type, Enum): + enum_name = model_name_map[field_type] + f_schema, schema_overrides = get_field_info_schema(field, schema_overrides) + f_schema.update(get_schema_ref(enum_name, ref_prefix, ref_template, schema_overrides)) + definitions[enum_name] = enum_process_schema(field_type, field=field) + elif is_namedtuple(field_type): + sub_schema, *_ = model_process_schema( + field_type.__pydantic_model__, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + field=field, + ) + items_schemas = list(sub_schema['properties'].values()) + f_schema.update( + { + 'type': 'array', + 'items': items_schemas, + 'minItems': len(items_schemas), + 'maxItems': len(items_schemas), + } + ) + elif not hasattr(field_type, '__pydantic_model__'): + add_field_type_to_schema(field_type, f_schema) + + modify_schema = getattr(field_type, '__modify_schema__', None) + if modify_schema: + _apply_modify_schema(modify_schema, field, f_schema) + + if f_schema: + return f_schema, definitions, nested_models + + # Handle dataclass-based models + if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): + field_type = field_type.__pydantic_model__ + + if issubclass(field_type, BaseModel): + model_name = model_name_map[field_type] + if field_type not in known_models: + sub_schema, sub_definitions, sub_nested_models = model_process_schema( + field_type, + by_alias=by_alias, + model_name_map=model_name_map, + ref_prefix=ref_prefix, + ref_template=ref_template, + known_models=known_models, + field=field, + ) + definitions.update(sub_definitions) + definitions[model_name] = sub_schema + nested_models.update(sub_nested_models) + else: + nested_models.add(model_name) + schema_ref = get_schema_ref(model_name, ref_prefix, ref_template, schema_overrides) + return schema_ref, definitions, nested_models + + # For generics with no args + args = get_args(field_type) + if args is not None and not args and Generic in field_type.__bases__: + return f_schema, definitions, nested_models + + raise ValueError(f'Value not declarable with JSON Schema, field: {field}') + + +def multitypes_literal_field_for_schema(values: Tuple[Any, ...], field: ModelField) -> ModelField: + """ + To support `Literal` with values of different types, we split it into multiple `Literal` with same type + e.g. `Literal['qwe', 'asd', 1, 2]` becomes `Union[Literal['qwe', 'asd'], Literal[1, 2]]` + """ + literal_distinct_types = defaultdict(list) + for v in values: + literal_distinct_types[v.__class__].append(v) + distinct_literals = (Literal[tuple(same_type_values)] for same_type_values in literal_distinct_types.values()) + + return ModelField( + name=field.name, + type_=Union[tuple(distinct_literals)], # type: ignore + class_validators=field.class_validators, + model_config=field.model_config, + default=field.default, + required=field.required, + alias=field.alias, + field_info=field.field_info, + ) + + +def encode_default(dft: Any) -> Any: + if isinstance(dft, Enum): + return dft.value + elif isinstance(dft, (int, float, str)): + return dft + elif isinstance(dft, (list, tuple)): + t = dft.__class__ + seq_args = (encode_default(v) for v in dft) + return t(*seq_args) if is_namedtuple(t) else t(seq_args) + elif isinstance(dft, dict): + return {encode_default(k): encode_default(v) for k, v in dft.items()} + elif dft is None: + return None + else: + return pydantic_encoder(dft) + + +_map_types_constraint: Dict[Any, Callable[..., type]] = {int: conint, float: confloat, Decimal: condecimal} + + +def get_annotation_from_field_info( + annotation: Any, field_info: FieldInfo, field_name: str, validate_assignment: bool = False +) -> Type[Any]: + """ + Get an annotation with validation implemented for numbers and strings based on the field_info. + :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr`` + :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema + :param field_name: name of the field for use in error messages + :param validate_assignment: default False, flag for BaseModel Config value of validate_assignment + :return: the same ``annotation`` if unmodified or a new annotation with validation in place + """ + constraints = field_info.get_constraints() + used_constraints: Set[str] = set() + if constraints: + annotation, used_constraints = get_annotation_with_constraints(annotation, field_info) + if validate_assignment: + used_constraints.add('allow_mutation') + + unused_constraints = constraints - used_constraints + if unused_constraints: + raise ValueError( + f'On field "{field_name}" the following field constraints are set but not enforced: ' + f'{", ".join(unused_constraints)}. ' + f'\nFor more details see https://pydantic-docs.helpmanual.io/usage/schema/#unenforced-field-constraints' + ) + + return annotation + + +def get_annotation_with_constraints(annotation: Any, field_info: FieldInfo) -> Tuple[Type[Any], Set[str]]: # noqa: C901 + """ + Get an annotation with used constraints implemented for numbers and strings based on the field_info. + + :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr`` + :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema + :return: the same ``annotation`` if unmodified or a new annotation along with the used constraints. + """ + used_constraints: Set[str] = set() + + def go(type_: Any) -> Type[Any]: + if ( + is_literal_type(type_) + or isinstance(type_, ForwardRef) + or lenient_issubclass(type_, (ConstrainedList, ConstrainedSet, ConstrainedFrozenSet)) + ): + return type_ + origin = get_origin(type_) + if origin is not None: + args: Tuple[Any, ...] = get_args(type_) + if any(isinstance(a, ForwardRef) for a in args): + # forward refs cause infinite recursion below + return type_ + + if origin is Annotated: + return go(args[0]) + if is_union(origin): + return Union[tuple(go(a) for a in args)] # type: ignore + + if issubclass(origin, List) and ( + field_info.min_items is not None + or field_info.max_items is not None + or field_info.unique_items is not None + ): + used_constraints.update({'min_items', 'max_items', 'unique_items'}) + return conlist( + go(args[0]), + min_items=field_info.min_items, + max_items=field_info.max_items, + unique_items=field_info.unique_items, + ) + + if issubclass(origin, Set) and (field_info.min_items is not None or field_info.max_items is not None): + used_constraints.update({'min_items', 'max_items'}) + return conset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items) + + if issubclass(origin, FrozenSet) and (field_info.min_items is not None or field_info.max_items is not None): + used_constraints.update({'min_items', 'max_items'}) + return confrozenset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items) + + for t in (Tuple, List, Set, FrozenSet, Sequence): + if issubclass(origin, t): # type: ignore + return t[tuple(go(a) for a in args)] # type: ignore + + if issubclass(origin, Dict): + return Dict[args[0], go(args[1])] # type: ignore + + attrs: Optional[Tuple[str, ...]] = None + constraint_func: Optional[Callable[..., type]] = None + if isinstance(type_, type): + if issubclass(type_, (SecretStr, SecretBytes)): + attrs = ('max_length', 'min_length') + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + elif issubclass(type_, str) and not issubclass(type_, (EmailStr, AnyUrl)): + attrs = ('max_length', 'min_length', 'regex') + if issubclass(type_, StrictStr): + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + else: + constraint_func = constr + elif issubclass(type_, bytes): + attrs = ('max_length', 'min_length', 'regex') + if issubclass(type_, StrictBytes): + + def constraint_func(**kw: Any) -> Type[Any]: + return type(type_.__name__, (type_,), kw) + + else: + constraint_func = conbytes + elif issubclass(type_, numeric_types) and not issubclass( + type_, + ( + ConstrainedInt, + ConstrainedFloat, + ConstrainedDecimal, + ConstrainedList, + ConstrainedSet, + ConstrainedFrozenSet, + bool, + ), + ): + # Is numeric type + attrs = ('gt', 'lt', 'ge', 'le', 'multiple_of') + if issubclass(type_, float): + attrs += ('allow_inf_nan',) + if issubclass(type_, Decimal): + attrs += ('max_digits', 'decimal_places') + numeric_type = next(t for t in numeric_types if issubclass(type_, t)) # pragma: no branch + constraint_func = _map_types_constraint[numeric_type] + + if attrs: + used_constraints.update(set(attrs)) + kwargs = { + attr_name: attr + for attr_name, attr in ((attr_name, getattr(field_info, attr_name)) for attr_name in attrs) + if attr is not None + } + if kwargs: + constraint_func = cast(Callable[..., type], constraint_func) + return constraint_func(**kwargs) + return type_ + + return go(annotation), used_constraints + + +def normalize_name(name: str) -> str: + """ + Normalizes the given name. This can be applied to either a model *or* enum. + """ + return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name) + + +class SkipField(Exception): + """ + Utility exception used to exclude fields from schema. + """ + + def __init__(self, message: str) -> None: + self.message = message diff --git a/libs/win/pydantic/tools.cp37-win_amd64.pyd b/libs/win/pydantic/tools.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..cd8cf09e5108c40583a2460d563cef654901575d GIT binary patch literal 64000 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 I-vAB#4>oV5EdT%j literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/tools.py b/libs/win/pydantic/tools.py new file mode 100644 index 00000000..9cdb4538 --- /dev/null +++ b/libs/win/pydantic/tools.py @@ -0,0 +1,92 @@ +import json +from functools import lru_cache +from pathlib import Path +from typing import TYPE_CHECKING, Any, Callable, Optional, Type, TypeVar, Union + +from .parse import Protocol, load_file, load_str_bytes +from .types import StrBytes +from .typing import display_as_type + +__all__ = ('parse_file_as', 'parse_obj_as', 'parse_raw_as', 'schema_of', 'schema_json_of') + +NameFactory = Union[str, Callable[[Type[Any]], str]] + +if TYPE_CHECKING: + from .typing import DictStrAny + + +def _generate_parsing_type_name(type_: Any) -> str: + return f'ParsingModel[{display_as_type(type_)}]' + + +@lru_cache(maxsize=2048) +def _get_parsing_type(type_: Any, *, type_name: Optional[NameFactory] = None) -> Any: + from pydantic.main import create_model + + if type_name is None: + type_name = _generate_parsing_type_name + if not isinstance(type_name, str): + type_name = type_name(type_) + return create_model(type_name, __root__=(type_, ...)) + + +T = TypeVar('T') + + +def parse_obj_as(type_: Type[T], obj: Any, *, type_name: Optional[NameFactory] = None) -> T: + model_type = _get_parsing_type(type_, type_name=type_name) # type: ignore[arg-type] + return model_type(__root__=obj).__root__ + + +def parse_file_as( + type_: Type[T], + path: Union[str, Path], + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, + type_name: Optional[NameFactory] = None, +) -> T: + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=json_loads, + ) + return parse_obj_as(type_, obj, type_name=type_name) + + +def parse_raw_as( + type_: Type[T], + b: StrBytes, + *, + content_type: str = None, + encoding: str = 'utf8', + proto: Protocol = None, + allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, + type_name: Optional[NameFactory] = None, +) -> T: + obj = load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=json_loads, + ) + return parse_obj_as(type_, obj, type_name=type_name) + + +def schema_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_kwargs: Any) -> 'DictStrAny': + """Generate a JSON schema (as dict) for the passed model or dynamically generated one""" + return _get_parsing_type(type_, type_name=title).schema(**schema_kwargs) + + +def schema_json_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_json_kwargs: Any) -> str: + """Generate a JSON schema (as JSON) for the passed model or dynamically generated one""" + return _get_parsing_type(type_, type_name=title).schema_json(**schema_json_kwargs) diff --git a/libs/win/pydantic/types.cp37-win_amd64.pyd b/libs/win/pydantic/types.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..da1767b205c2f1ff505717bd1794c0190f8cfa3c GIT binary patch literal 354816 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

    I%*h4zcfJD38^fy1!%j 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 L`1-&9Xdd{#_$=9^ literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/validators.py b/libs/win/pydantic/validators.py new file mode 100644 index 00000000..fb6d0418 --- /dev/null +++ b/libs/win/pydantic/validators.py @@ -0,0 +1,765 @@ +import math +import re +from collections import OrderedDict, deque +from collections.abc import Hashable as CollectionsHashable +from datetime import date, datetime, time, timedelta +from decimal import Decimal, DecimalException +from enum import Enum, IntEnum +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Deque, + Dict, + ForwardRef, + FrozenSet, + Generator, + Hashable, + List, + NamedTuple, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, +) +from uuid import UUID + +from . import errors +from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time +from .typing import ( + AnyCallable, + all_literal_values, + display_as_type, + get_class, + is_callable_type, + is_literal_type, + is_namedtuple, + is_none_type, + is_typeddict, +) +from .utils import almost_equal_floats, lenient_issubclass, sequence_like + +if TYPE_CHECKING: + from typing_extensions import Literal, TypedDict + + from .config import BaseConfig + from .fields import ModelField + from .types import ConstrainedDecimal, ConstrainedFloat, ConstrainedInt + + ConstrainedNumber = Union[ConstrainedDecimal, ConstrainedFloat, ConstrainedInt] + AnyOrderedDict = OrderedDict[Any, Any] + Number = Union[int, float, Decimal] + StrBytes = Union[str, bytes] + + +def str_validator(v: Any) -> Union[str]: + if isinstance(v, str): + if isinstance(v, Enum): + return v.value + else: + return v + elif isinstance(v, (float, int, Decimal)): + # is there anything else we want to add here? If you think so, create an issue. + return str(v) + elif isinstance(v, (bytes, bytearray)): + return v.decode() + else: + raise errors.StrError() + + +def strict_str_validator(v: Any) -> Union[str]: + if isinstance(v, str) and not isinstance(v, Enum): + return v + raise errors.StrError() + + +def bytes_validator(v: Any) -> Union[bytes]: + if isinstance(v, bytes): + return v + elif isinstance(v, bytearray): + return bytes(v) + elif isinstance(v, str): + return v.encode() + elif isinstance(v, (float, int, Decimal)): + return str(v).encode() + else: + raise errors.BytesError() + + +def strict_bytes_validator(v: Any) -> Union[bytes]: + if isinstance(v, bytes): + return v + elif isinstance(v, bytearray): + return bytes(v) + else: + raise errors.BytesError() + + +BOOL_FALSE = {0, '0', 'off', 'f', 'false', 'n', 'no'} +BOOL_TRUE = {1, '1', 'on', 't', 'true', 'y', 'yes'} + + +def bool_validator(v: Any) -> bool: + if v is True or v is False: + return v + if isinstance(v, bytes): + v = v.decode() + if isinstance(v, str): + v = v.lower() + try: + if v in BOOL_TRUE: + return True + if v in BOOL_FALSE: + return False + except TypeError: + raise errors.BoolError() + raise errors.BoolError() + + +# matches the default limit cpython, see https://github.com/python/cpython/pull/96500 +max_str_int = 4_300 + + +def int_validator(v: Any) -> int: + if isinstance(v, int) and not (v is True or v is False): + return v + + # see https://github.com/pydantic/pydantic/issues/1477 and in turn, https://github.com/python/cpython/issues/95778 + # this check should be unnecessary once patch releases are out for 3.7, 3.8, 3.9 and 3.10 + # but better to check here until then. + # NOTICE: this does not fully protect user from the DOS risk since the standard library JSON implementation + # (and other std lib modules like xml) use `int()` and are likely called before this, the best workaround is to + # 1. update to the latest patch release of python once released, 2. use a different JSON library like ujson + if isinstance(v, (str, bytes, bytearray)) and len(v) > max_str_int: + raise errors.IntegerError() + + try: + return int(v) + except (TypeError, ValueError, OverflowError): + raise errors.IntegerError() + + +def strict_int_validator(v: Any) -> int: + if isinstance(v, int) and not (v is True or v is False): + return v + raise errors.IntegerError() + + +def float_validator(v: Any) -> float: + if isinstance(v, float): + return v + + try: + return float(v) + except (TypeError, ValueError): + raise errors.FloatError() + + +def strict_float_validator(v: Any) -> float: + if isinstance(v, float): + return v + raise errors.FloatError() + + +def float_finite_validator(v: 'Number', field: 'ModelField', config: 'BaseConfig') -> 'Number': + allow_inf_nan = getattr(field.type_, 'allow_inf_nan', None) + if allow_inf_nan is None: + allow_inf_nan = config.allow_inf_nan + + if allow_inf_nan is False and (math.isnan(v) or math.isinf(v)): + raise errors.NumberNotFiniteError() + return v + + +def number_multiple_validator(v: 'Number', field: 'ModelField') -> 'Number': + field_type: ConstrainedNumber = field.type_ + if field_type.multiple_of is not None: + mod = float(v) / float(field_type.multiple_of) % 1 + if not almost_equal_floats(mod, 0.0) and not almost_equal_floats(mod, 1.0): + raise errors.NumberNotMultipleError(multiple_of=field_type.multiple_of) + return v + + +def number_size_validator(v: 'Number', field: 'ModelField') -> 'Number': + field_type: ConstrainedNumber = field.type_ + if field_type.gt is not None and not v > field_type.gt: + raise errors.NumberNotGtError(limit_value=field_type.gt) + elif field_type.ge is not None and not v >= field_type.ge: + raise errors.NumberNotGeError(limit_value=field_type.ge) + + if field_type.lt is not None and not v < field_type.lt: + raise errors.NumberNotLtError(limit_value=field_type.lt) + if field_type.le is not None and not v <= field_type.le: + raise errors.NumberNotLeError(limit_value=field_type.le) + + return v + + +def constant_validator(v: 'Any', field: 'ModelField') -> 'Any': + """Validate ``const`` fields. + + The value provided for a ``const`` field must be equal to the default value + of the field. This is to support the keyword of the same name in JSON + Schema. + """ + if v != field.default: + raise errors.WrongConstantError(given=v, permitted=[field.default]) + + return v + + +def anystr_length_validator(v: 'StrBytes', config: 'BaseConfig') -> 'StrBytes': + v_len = len(v) + + min_length = config.min_anystr_length + if v_len < min_length: + raise errors.AnyStrMinLengthError(limit_value=min_length) + + max_length = config.max_anystr_length + if max_length is not None and v_len > max_length: + raise errors.AnyStrMaxLengthError(limit_value=max_length) + + return v + + +def anystr_strip_whitespace(v: 'StrBytes') -> 'StrBytes': + return v.strip() + + +def anystr_upper(v: 'StrBytes') -> 'StrBytes': + return v.upper() + + +def anystr_lower(v: 'StrBytes') -> 'StrBytes': + return v.lower() + + +def ordered_dict_validator(v: Any) -> 'AnyOrderedDict': + if isinstance(v, OrderedDict): + return v + + try: + return OrderedDict(v) + except (TypeError, ValueError): + raise errors.DictError() + + +def dict_validator(v: Any) -> Dict[Any, Any]: + if isinstance(v, dict): + return v + + try: + return dict(v) + except (TypeError, ValueError): + raise errors.DictError() + + +def list_validator(v: Any) -> List[Any]: + if isinstance(v, list): + return v + elif sequence_like(v): + return list(v) + else: + raise errors.ListError() + + +def tuple_validator(v: Any) -> Tuple[Any, ...]: + if isinstance(v, tuple): + return v + elif sequence_like(v): + return tuple(v) + else: + raise errors.TupleError() + + +def set_validator(v: Any) -> Set[Any]: + if isinstance(v, set): + return v + elif sequence_like(v): + return set(v) + else: + raise errors.SetError() + + +def frozenset_validator(v: Any) -> FrozenSet[Any]: + if isinstance(v, frozenset): + return v + elif sequence_like(v): + return frozenset(v) + else: + raise errors.FrozenSetError() + + +def deque_validator(v: Any) -> Deque[Any]: + if isinstance(v, deque): + return v + elif sequence_like(v): + return deque(v) + else: + raise errors.DequeError() + + +def enum_member_validator(v: Any, field: 'ModelField', config: 'BaseConfig') -> Enum: + try: + enum_v = field.type_(v) + except ValueError: + # field.type_ should be an enum, so will be iterable + raise errors.EnumMemberError(enum_values=list(field.type_)) + return enum_v.value if config.use_enum_values else enum_v + + +def uuid_validator(v: Any, field: 'ModelField') -> UUID: + try: + if isinstance(v, str): + v = UUID(v) + elif isinstance(v, (bytes, bytearray)): + try: + v = UUID(v.decode()) + except ValueError: + # 16 bytes in big-endian order as the bytes argument fail + # the above check + v = UUID(bytes=v) + except ValueError: + raise errors.UUIDError() + + if not isinstance(v, UUID): + raise errors.UUIDError() + + required_version = getattr(field.type_, '_required_version', None) + if required_version and v.version != required_version: + raise errors.UUIDVersionError(required_version=required_version) + + return v + + +def decimal_validator(v: Any) -> Decimal: + if isinstance(v, Decimal): + return v + elif isinstance(v, (bytes, bytearray)): + v = v.decode() + + v = str(v).strip() + + try: + v = Decimal(v) + except DecimalException: + raise errors.DecimalError() + + if not v.is_finite(): + raise errors.DecimalIsNotFiniteError() + + return v + + +def hashable_validator(v: Any) -> Hashable: + if isinstance(v, Hashable): + return v + + raise errors.HashableError() + + +def ip_v4_address_validator(v: Any) -> IPv4Address: + if isinstance(v, IPv4Address): + return v + + try: + return IPv4Address(v) + except ValueError: + raise errors.IPv4AddressError() + + +def ip_v6_address_validator(v: Any) -> IPv6Address: + if isinstance(v, IPv6Address): + return v + + try: + return IPv6Address(v) + except ValueError: + raise errors.IPv6AddressError() + + +def ip_v4_network_validator(v: Any) -> IPv4Network: + """ + Assume IPv4Network initialised with a default ``strict`` argument + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network + """ + if isinstance(v, IPv4Network): + return v + + try: + return IPv4Network(v) + except ValueError: + raise errors.IPv4NetworkError() + + +def ip_v6_network_validator(v: Any) -> IPv6Network: + """ + Assume IPv6Network initialised with a default ``strict`` argument + + See more: + https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network + """ + if isinstance(v, IPv6Network): + return v + + try: + return IPv6Network(v) + except ValueError: + raise errors.IPv6NetworkError() + + +def ip_v4_interface_validator(v: Any) -> IPv4Interface: + if isinstance(v, IPv4Interface): + return v + + try: + return IPv4Interface(v) + except ValueError: + raise errors.IPv4InterfaceError() + + +def ip_v6_interface_validator(v: Any) -> IPv6Interface: + if isinstance(v, IPv6Interface): + return v + + try: + return IPv6Interface(v) + except ValueError: + raise errors.IPv6InterfaceError() + + +def path_validator(v: Any) -> Path: + if isinstance(v, Path): + return v + + try: + return Path(v) + except TypeError: + raise errors.PathError() + + +def path_exists_validator(v: Any) -> Path: + if not v.exists(): + raise errors.PathNotExistsError(path=v) + + return v + + +def callable_validator(v: Any) -> AnyCallable: + """ + Perform a simple check if the value is callable. + + Note: complete matching of argument type hints and return types is not performed + """ + if callable(v): + return v + + raise errors.CallableError(value=v) + + +def enum_validator(v: Any) -> Enum: + if isinstance(v, Enum): + return v + + raise errors.EnumError(value=v) + + +def int_enum_validator(v: Any) -> IntEnum: + if isinstance(v, IntEnum): + return v + + raise errors.IntEnumError(value=v) + + +def make_literal_validator(type_: Any) -> Callable[[Any], Any]: + permitted_choices = all_literal_values(type_) + + # To have a O(1) complexity and still return one of the values set inside the `Literal`, + # we create a dict with the set values (a set causes some problems with the way intersection works). + # In some cases the set value and checked value can indeed be different (see `test_literal_validator_str_enum`) + allowed_choices = {v: v for v in permitted_choices} + + def literal_validator(v: Any) -> Any: + try: + return allowed_choices[v] + except KeyError: + raise errors.WrongConstantError(given=v, permitted=permitted_choices) + + return literal_validator + + +def constr_length_validator(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + v_len = len(v) + + min_length = field.type_.min_length if field.type_.min_length is not None else config.min_anystr_length + if v_len < min_length: + raise errors.AnyStrMinLengthError(limit_value=min_length) + + max_length = field.type_.max_length if field.type_.max_length is not None else config.max_anystr_length + if max_length is not None and v_len > max_length: + raise errors.AnyStrMaxLengthError(limit_value=max_length) + + return v + + +def constr_strip_whitespace(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + strip_whitespace = field.type_.strip_whitespace or config.anystr_strip_whitespace + if strip_whitespace: + v = v.strip() + + return v + + +def constr_upper(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + upper = field.type_.to_upper or config.anystr_upper + if upper: + v = v.upper() + + return v + + +def constr_lower(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes': + lower = field.type_.to_lower or config.anystr_lower + if lower: + v = v.lower() + return v + + +def validate_json(v: Any, config: 'BaseConfig') -> Any: + if v is None: + # pass None through to other validators + return v + try: + return config.json_loads(v) # type: ignore + except ValueError: + raise errors.JsonError() + except TypeError: + raise errors.JsonTypeError() + + +T = TypeVar('T') + + +def make_arbitrary_type_validator(type_: Type[T]) -> Callable[[T], T]: + def arbitrary_type_validator(v: Any) -> T: + if isinstance(v, type_): + return v + raise errors.ArbitraryTypeError(expected_arbitrary_type=type_) + + return arbitrary_type_validator + + +def make_class_validator(type_: Type[T]) -> Callable[[Any], Type[T]]: + def class_validator(v: Any) -> Type[T]: + if lenient_issubclass(v, type_): + return v + raise errors.SubclassError(expected_class=type_) + + return class_validator + + +def any_class_validator(v: Any) -> Type[T]: + if isinstance(v, type): + return v + raise errors.ClassError() + + +def none_validator(v: Any) -> 'Literal[None]': + if v is None: + return v + raise errors.NotNoneError() + + +def pattern_validator(v: Any) -> Pattern[str]: + if isinstance(v, Pattern): + return v + + str_value = str_validator(v) + + try: + return re.compile(str_value) + except re.error: + raise errors.PatternError() + + +NamedTupleT = TypeVar('NamedTupleT', bound=NamedTuple) + + +def make_namedtuple_validator( + namedtuple_cls: Type[NamedTupleT], config: Type['BaseConfig'] +) -> Callable[[Tuple[Any, ...]], NamedTupleT]: + from .annotated_types import create_model_from_namedtuple + + NamedTupleModel = create_model_from_namedtuple( + namedtuple_cls, + __config__=config, + __module__=namedtuple_cls.__module__, + ) + namedtuple_cls.__pydantic_model__ = NamedTupleModel # type: ignore[attr-defined] + + def namedtuple_validator(values: Tuple[Any, ...]) -> NamedTupleT: + annotations = NamedTupleModel.__annotations__ + + if len(values) > len(annotations): + raise errors.ListMaxLengthError(limit_value=len(annotations)) + + dict_values: Dict[str, Any] = dict(zip(annotations, values)) + validated_dict_values: Dict[str, Any] = dict(NamedTupleModel(**dict_values)) + return namedtuple_cls(**validated_dict_values) + + return namedtuple_validator + + +def make_typeddict_validator( + typeddict_cls: Type['TypedDict'], config: Type['BaseConfig'] # type: ignore[valid-type] +) -> Callable[[Any], Dict[str, Any]]: + from .annotated_types import create_model_from_typeddict + + TypedDictModel = create_model_from_typeddict( + typeddict_cls, + __config__=config, + __module__=typeddict_cls.__module__, + ) + typeddict_cls.__pydantic_model__ = TypedDictModel # type: ignore[attr-defined] + + def typeddict_validator(values: 'TypedDict') -> Dict[str, Any]: # type: ignore[valid-type] + return TypedDictModel.parse_obj(values).dict(exclude_unset=True) + + return typeddict_validator + + +class IfConfig: + def __init__(self, validator: AnyCallable, *config_attr_names: str, ignored_value: Any = False) -> None: + self.validator = validator + self.config_attr_names = config_attr_names + self.ignored_value = ignored_value + + def check(self, config: Type['BaseConfig']) -> bool: + return any(getattr(config, name) not in {None, self.ignored_value} for name in self.config_attr_names) + + +# order is important here, for example: bool is a subclass of int so has to come first, datetime before date same, +# IPv4Interface before IPv4Address, etc +_VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [ + (IntEnum, [int_validator, enum_member_validator]), + (Enum, [enum_member_validator]), + ( + str, + [ + str_validator, + IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'), + IfConfig(anystr_upper, 'anystr_upper'), + IfConfig(anystr_lower, 'anystr_lower'), + IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'), + ], + ), + ( + bytes, + [ + bytes_validator, + IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'), + IfConfig(anystr_upper, 'anystr_upper'), + IfConfig(anystr_lower, 'anystr_lower'), + IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'), + ], + ), + (bool, [bool_validator]), + (int, [int_validator]), + (float, [float_validator, IfConfig(float_finite_validator, 'allow_inf_nan', ignored_value=True)]), + (Path, [path_validator]), + (datetime, [parse_datetime]), + (date, [parse_date]), + (time, [parse_time]), + (timedelta, [parse_duration]), + (OrderedDict, [ordered_dict_validator]), + (dict, [dict_validator]), + (list, [list_validator]), + (tuple, [tuple_validator]), + (set, [set_validator]), + (frozenset, [frozenset_validator]), + (deque, [deque_validator]), + (UUID, [uuid_validator]), + (Decimal, [decimal_validator]), + (IPv4Interface, [ip_v4_interface_validator]), + (IPv6Interface, [ip_v6_interface_validator]), + (IPv4Address, [ip_v4_address_validator]), + (IPv6Address, [ip_v6_address_validator]), + (IPv4Network, [ip_v4_network_validator]), + (IPv6Network, [ip_v6_network_validator]), +] + + +def find_validators( # noqa: C901 (ignore complexity) + type_: Type[Any], config: Type['BaseConfig'] +) -> Generator[AnyCallable, None, None]: + from .dataclasses import is_builtin_dataclass, make_dataclass_validator + + if type_ is Any or type_ is object: + return + type_type = type_.__class__ + if type_type == ForwardRef or type_type == TypeVar: + return + + if is_none_type(type_): + yield none_validator + return + if type_ is Pattern or type_ is re.Pattern: + yield pattern_validator + return + if type_ is Hashable or type_ is CollectionsHashable: + yield hashable_validator + return + if is_callable_type(type_): + yield callable_validator + return + if is_literal_type(type_): + yield make_literal_validator(type_) + return + if is_builtin_dataclass(type_): + yield from make_dataclass_validator(type_, config) + return + if type_ is Enum: + yield enum_validator + return + if type_ is IntEnum: + yield int_enum_validator + return + if is_namedtuple(type_): + yield tuple_validator + yield make_namedtuple_validator(type_, config) + return + if is_typeddict(type_): + yield make_typeddict_validator(type_, config) + return + + class_ = get_class(type_) + if class_ is not None: + if class_ is not Any and isinstance(class_, type): + yield make_class_validator(class_) + else: + yield any_class_validator + return + + for val_type, validators in _VALIDATORS: + try: + if issubclass(type_, val_type): + for v in validators: + if isinstance(v, IfConfig): + if v.check(config): + yield v.validator + else: + yield v + return + except TypeError: + raise RuntimeError(f'error checking inheritance of {type_!r} (type: {display_as_type(type_)})') + + if config.arbitrary_types_allowed: + yield make_arbitrary_type_validator(type_) + else: + raise RuntimeError(f'no validator found for {type_}, see `arbitrary_types_allowed` in Config') diff --git a/libs/win/pydantic/version.cp37-win_amd64.pyd b/libs/win/pydantic/version.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..91395862481d52ead2a96f67b13fa8024e220052 GIT binary patch literal 53760 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/version.py b/libs/win/pydantic/version.py new file mode 100644 index 00000000..32c61633 --- /dev/null +++ b/libs/win/pydantic/version.py @@ -0,0 +1,38 @@ +__all__ = 'compiled', 'VERSION', 'version_info' + +VERSION = '1.10.2' + +try: + import cython # type: ignore +except ImportError: + compiled: bool = False +else: # pragma: no cover + try: + compiled = cython.compiled + except AttributeError: + compiled = False + + +def version_info() -> str: + import platform + import sys + from importlib import import_module + from pathlib import Path + + optional_deps = [] + for p in ('devtools', 'dotenv', 'email-validator', 'typing-extensions'): + try: + import_module(p.replace('-', '_')) + except ImportError: + continue + optional_deps.append(p) + + info = { + 'pydantic version': VERSION, + 'pydantic compiled': compiled, + 'install path': Path(__file__).resolve().parent, + 'python version': sys.version, + 'platform': platform.platform(), + 'optional deps. installed': optional_deps, + } + return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items()) diff --git a/libs/win/scripts/watch-changes.py b/libs/win/scripts/watch-changes.py new file mode 100644 index 00000000..72b48686 --- /dev/null +++ b/libs/win/scripts/watch-changes.py @@ -0,0 +1,35 @@ +import traceback +import sys +import logging + +from jaraco.windows.filesystem import change + +logging.basicConfig(level=logging.INFO) + + +def long_handler(file): + try: + with open(file, 'rb') as f: + data = f.read() + print("read", len(data), "bytes from", file) + except Exception: + traceback.print_exc() + + +def main(): + try: + watch() + except KeyboardInterrupt: + pass + + +def watch(): + notifier = change.BlockingNotifier(sys.argv[1]) + notifier.watch_subtree = True + + for ch in notifier.get_changed_files(): + long_handler(ch) + + +if __name__ == '__main__': + main() diff --git a/libs/win/six.py b/libs/win/six.py deleted file mode 100644 index 89b2188f..00000000 --- a/libs/win/six.py +++ /dev/null @@ -1,952 +0,0 @@ -# Copyright (c) 2010-2018 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.12.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, text_type): - return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/libs/win/test_path.py b/libs/win/test_path.py deleted file mode 100644 index 2a7ddb8f..00000000 --- a/libs/win/test_path.py +++ /dev/null @@ -1,1258 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Tests for the path module. - -This suite runs on Linux, OS X, and Windows right now. To extend the -platform support, just add appropriate pathnames for your -platform (os.name) in each place where the p() function is called. -Then report the result. If you can't get the test to run at all on -your platform, there's probably a bug in path.py -- please report the issue -in the issue tracker at https://github.com/jaraco/path.py. - -TestScratchDir.test_touch() takes a while to run. It sleeps a few -seconds to allow some time to pass between calls to check the modify -time on files. -""" - -from __future__ import unicode_literals, absolute_import, print_function - -import codecs -import os -import sys -import shutil -import time -import types -import ntpath -import posixpath -import textwrap -import platform -import importlib -import operator -import datetime -import subprocess -import re - -import pytest -import packaging.version - -import path -from path import TempDir -from path import matchers -from path import SpecialResolver -from path import Multi - -Path = None - - -def p(**choices): - """ Choose a value from several possible values, based on os.name """ - return choices[os.name] - - -@pytest.fixture(autouse=True, params=[path.Path]) -def path_class(request, monkeypatch): - """ - Invoke tests on any number of Path classes. - """ - monkeypatch.setitem(globals(), 'Path', request.param) - - -def mac_version(target, comparator=operator.ge): - """ - Return True if on a Mac whose version passes the comparator. - """ - current_ver = packaging.version.parse(platform.mac_ver()[0]) - target_ver = packaging.version.parse(target) - return ( - platform.system() == 'Darwin' - and comparator(current_ver, target_ver) - ) - - -class TestBasics: - def test_relpath(self): - root = Path(p(nt='C:\\', posix='/')) - foo = root / 'foo' - quux = foo / 'quux' - bar = foo / 'bar' - boz = bar / 'Baz' / 'Boz' - up = Path(os.pardir) - - # basics - assert root.relpathto(boz) == Path('foo') / 'bar' / 'Baz' / 'Boz' - assert bar.relpathto(boz) == Path('Baz') / 'Boz' - assert quux.relpathto(boz) == up / 'bar' / 'Baz' / 'Boz' - assert boz.relpathto(quux) == up / up / up / 'quux' - assert boz.relpathto(bar) == up / up - - # Path is not the first element in concatenation - assert root.relpathto(boz) == 'foo' / Path('bar') / 'Baz' / 'Boz' - - # x.relpathto(x) == curdir - assert root.relpathto(root) == os.curdir - assert boz.relpathto(boz) == os.curdir - # Make sure case is properly noted (or ignored) - assert boz.relpathto(boz.normcase()) == os.curdir - - # relpath() - cwd = Path(os.getcwd()) - assert boz.relpath() == cwd.relpathto(boz) - - if os.name == 'nt': - # Check relpath across drives. - d = Path('D:\\') - assert d.relpathto(boz) == boz - - def test_construction_from_none(self): - """ - - """ - try: - Path(None) - except TypeError: - pass - else: - raise Exception("DID NOT RAISE") - - def test_construction_from_int(self): - """ - Path class will construct a path as a string of the number - """ - assert Path(1) == '1' - - def test_string_compatibility(self): - """ Test compatibility with ordinary strings. """ - x = Path('xyzzy') - assert x == 'xyzzy' - assert x == str('xyzzy') - - # sorting - items = [Path('fhj'), - Path('fgh'), - 'E', - Path('d'), - 'A', - Path('B'), - 'c'] - items.sort() - assert items == ['A', 'B', 'E', 'c', 'd', 'fgh', 'fhj'] - - # Test p1/p1. - p1 = Path("foo") - p2 = Path("bar") - assert p1 / p2 == p(nt='foo\\bar', posix='foo/bar') - - def test_properties(self): - # Create sample path object. - f = p(nt='C:\\Program Files\\Python\\Lib\\xyzzy.py', - posix='/usr/local/python/lib/xyzzy.py') - f = Path(f) - - # .parent - nt_lib = 'C:\\Program Files\\Python\\Lib' - posix_lib = '/usr/local/python/lib' - expected = p(nt=nt_lib, posix=posix_lib) - assert f.parent == expected - - # .name - assert f.name == 'xyzzy.py' - assert f.parent.name == p(nt='Lib', posix='lib') - - # .ext - assert f.ext == '.py' - assert f.parent.ext == '' - - # .drive - assert f.drive == p(nt='C:', posix='') - - def test_methods(self): - # .abspath() - assert Path(os.curdir).abspath() == os.getcwd() - - # .getcwd() - cwd = Path.getcwd() - assert isinstance(cwd, Path) - assert cwd == os.getcwd() - - def test_UNC(self): - if hasattr(os.path, 'splitunc'): - p = Path(r'\\python1\share1\dir1\file1.txt') - assert p.uncshare == r'\\python1\share1' - assert p.splitunc() == os.path.splitunc(str(p)) - - def test_explicit_module(self): - """ - The user may specify an explicit path module to use. - """ - nt_ok = Path.using_module(ntpath)(r'foo\bar\baz') - posix_ok = Path.using_module(posixpath)(r'foo/bar/baz') - posix_wrong = Path.using_module(posixpath)(r'foo\bar\baz') - - assert nt_ok.dirname() == r'foo\bar' - assert posix_ok.dirname() == r'foo/bar' - assert posix_wrong.dirname() == '' - - assert nt_ok / 'quux' == r'foo\bar\baz\quux' - assert posix_ok / 'quux' == r'foo/bar/baz/quux' - - def test_explicit_module_classes(self): - """ - Multiple calls to path.using_module should produce the same class. - """ - nt_path = Path.using_module(ntpath) - assert nt_path is Path.using_module(ntpath) - assert nt_path.__name__ == 'Path_ntpath' - - def test_joinpath_on_instance(self): - res = Path('foo') - foo_bar = res.joinpath('bar') - assert foo_bar == p(nt='foo\\bar', posix='foo/bar') - - def test_joinpath_to_nothing(self): - res = Path('foo') - assert res.joinpath() == res - - def test_joinpath_on_class(self): - "Construct a path from a series of strings" - foo_bar = Path.joinpath('foo', 'bar') - assert foo_bar == p(nt='foo\\bar', posix='foo/bar') - - def test_joinpath_fails_on_empty(self): - "It doesn't make sense to join nothing at all" - try: - Path.joinpath() - except TypeError: - pass - else: - raise Exception("did not raise") - - def test_joinpath_returns_same_type(self): - path_posix = Path.using_module(posixpath) - res = path_posix.joinpath('foo') - assert isinstance(res, path_posix) - res2 = res.joinpath('bar') - assert isinstance(res2, path_posix) - assert res2 == 'foo/bar' - - -class TestPerformance: - @pytest.mark.skipif( - path.PY2, - reason="Tests fail frequently on Python 2; see #153") - def test_import_time(self, monkeypatch): - """ - Import of path.py should take less than 100ms. - - Run tests in a subprocess to isolate from test suite overhead. - """ - cmd = [ - sys.executable, - '-m', 'timeit', - '-n', '1', - '-r', '1', - 'import path', - ] - res = subprocess.check_output(cmd, universal_newlines=True) - dur = re.search(r'(\d+) msec per loop', res).group(1) - limit = datetime.timedelta(milliseconds=100) - duration = datetime.timedelta(milliseconds=int(dur)) - assert duration < limit - - -class TestSelfReturn: - """ - Some methods don't necessarily return any value (e.g. makedirs, - makedirs_p, rename, mkdir, touch, chroot). These methods should return - self anyhow to allow methods to be chained. - """ - def test_makedirs_p(self, tmpdir): - """ - Path('foo').makedirs_p() == Path('foo') - """ - p = Path(tmpdir) / "newpath" - ret = p.makedirs_p() - assert p == ret - - def test_makedirs_p_extant(self, tmpdir): - p = Path(tmpdir) - ret = p.makedirs_p() - assert p == ret - - def test_rename(self, tmpdir): - p = Path(tmpdir) / "somefile" - p.touch() - target = Path(tmpdir) / "otherfile" - ret = p.rename(target) - assert target == ret - - def test_mkdir(self, tmpdir): - p = Path(tmpdir) / "newdir" - ret = p.mkdir() - assert p == ret - - def test_touch(self, tmpdir): - p = Path(tmpdir) / "empty file" - ret = p.touch() - assert p == ret - - -class TestScratchDir: - """ - Tests that run in a temporary directory (does not test TempDir class) - """ - def test_context_manager(self, tmpdir): - """Can be used as context manager for chdir.""" - d = Path(tmpdir) - subdir = d / 'subdir' - subdir.makedirs() - old_dir = os.getcwd() - with subdir: - assert os.getcwd() == os.path.realpath(subdir) - assert os.getcwd() == old_dir - - def test_touch(self, tmpdir): - # NOTE: This test takes a long time to run (~10 seconds). - # It sleeps several seconds because on Windows, the resolution - # of a file's mtime and ctime is about 2 seconds. - # - # atime isn't tested because on Windows the resolution of atime - # is something like 24 hours. - - threshold = 1 - - d = Path(tmpdir) - f = d / 'test.txt' - t0 = time.time() - threshold - f.touch() - t1 = time.time() + threshold - - assert f.exists() - assert f.isfile() - assert f.size == 0 - assert t0 <= f.mtime <= t1 - if hasattr(os.path, 'getctime'): - ct = f.ctime - assert t0 <= ct <= t1 - - time.sleep(threshold * 2) - fobj = open(f, 'ab') - fobj.write('some bytes'.encode('utf-8')) - fobj.close() - - time.sleep(threshold * 2) - t2 = time.time() - threshold - f.touch() - t3 = time.time() + threshold - - assert t0 <= t1 < t2 <= t3 # sanity check - - assert f.exists() - assert f.isfile() - assert f.size == 10 - assert t2 <= f.mtime <= t3 - if hasattr(os.path, 'getctime'): - ct2 = f.ctime - if os.name == 'nt': - # On Windows, "ctime" is CREATION time - assert ct == ct2 - assert ct2 < t2 - else: - assert ( - # ctime is unchanged - ct == ct2 or - # ctime is approximately the mtime - ct2 == pytest.approx(f.mtime, 0.001) - ) - - def test_listing(self, tmpdir): - d = Path(tmpdir) - assert d.listdir() == [] - - f = 'testfile.txt' - af = d / f - assert af == os.path.join(d, f) - af.touch() - try: - assert af.exists() - - assert d.listdir() == [af] - - # .glob() - assert d.glob('testfile.txt') == [af] - assert d.glob('test*.txt') == [af] - assert d.glob('*.txt') == [af] - assert d.glob('*txt') == [af] - assert d.glob('*') == [af] - assert d.glob('*.html') == [] - assert d.glob('testfile') == [] - - # .iglob matches .glob but as an iterator. - assert list(d.iglob('*')) == d.glob('*') - assert isinstance(d.iglob('*'), types.GeneratorType) - - finally: - af.remove() - - # Try a test with 20 files - files = [d / ('%d.txt' % i) for i in range(20)] - for f in files: - fobj = open(f, 'w') - fobj.write('some text\n') - fobj.close() - try: - files2 = d.listdir() - files.sort() - files2.sort() - assert files == files2 - finally: - for f in files: - try: - f.remove() - except Exception: - pass - - @pytest.mark.xfail( - mac_version('10.13'), - reason="macOS disallows invalid encodings", - ) - @pytest.mark.xfail( - platform.system() == 'Windows' and path.PY3, - reason="Can't write latin characters. See #133", - ) - def test_listdir_other_encoding(self, tmpdir): - """ - Some filesystems allow non-character sequences in path names. - ``.listdir`` should still function in this case. - See issue #61 for details. - """ - assert Path(tmpdir).listdir() == [] - tmpdir_bytes = str(tmpdir).encode('ascii') - - filename = 'r\xe9\xf1emi'.encode('latin-1') - pathname = os.path.join(tmpdir_bytes, filename) - with open(pathname, 'wb'): - pass - # first demonstrate that os.listdir works - assert os.listdir(tmpdir_bytes) - - # now try with path.py - results = Path(tmpdir).listdir() - assert len(results) == 1 - res, = results - assert isinstance(res, Path) - # OS X seems to encode the bytes in the filename as %XX characters. - if platform.system() == 'Darwin': - assert res.basename() == 'r%E9%F1emi' - return - assert len(res.basename()) == len(filename) - - def test_makedirs(self, tmpdir): - d = Path(tmpdir) - - # Placeholder file so that when removedirs() is called, - # it doesn't remove the temporary directory itself. - tempf = d / 'temp.txt' - tempf.touch() - try: - foo = d / 'foo' - boz = foo / 'bar' / 'baz' / 'boz' - boz.makedirs() - try: - assert boz.isdir() - finally: - boz.removedirs() - assert not foo.exists() - assert d.exists() - - foo.mkdir(0o750) - boz.makedirs(0o700) - try: - assert boz.isdir() - finally: - boz.removedirs() - assert not foo.exists() - assert d.exists() - finally: - os.remove(tempf) - - def assertSetsEqual(self, a, b): - ad = {} - - for i in a: - ad[i] = None - - bd = {} - - for i in b: - bd[i] = None - - assert ad == bd - - def test_shutil(self, tmpdir): - # Note: This only tests the methods exist and do roughly what - # they should, neglecting the details as they are shutil's - # responsibility. - - d = Path(tmpdir) - testDir = d / 'testdir' - testFile = testDir / 'testfile.txt' - testA = testDir / 'A' - testCopy = testA / 'testcopy.txt' - testLink = testA / 'testlink.txt' - testB = testDir / 'B' - testC = testB / 'C' - testCopyOfLink = testC / testA.relpathto(testLink) - - # Create test dirs and a file - testDir.mkdir() - testA.mkdir() - testB.mkdir() - - f = open(testFile, 'w') - f.write('x' * 10000) - f.close() - - # Test simple file copying. - testFile.copyfile(testCopy) - assert testCopy.isfile() - assert testFile.bytes() == testCopy.bytes() - - # Test copying into a directory. - testCopy2 = testA / testFile.name - testFile.copy(testA) - assert testCopy2.isfile() - assert testFile.bytes() == testCopy2.bytes() - - # Make a link for the next test to use. - if hasattr(os, 'symlink'): - testFile.symlink(testLink) - else: - testFile.copy(testLink) # fallback - - # Test copying directory tree. - testA.copytree(testC) - assert testC.isdir() - self.assertSetsEqual( - testC.listdir(), - [testC / testCopy.name, - testC / testFile.name, - testCopyOfLink]) - assert not testCopyOfLink.islink() - - # Clean up for another try. - testC.rmtree() - assert not testC.exists() - - # Copy again, preserving symlinks. - testA.copytree(testC, True) - assert testC.isdir() - self.assertSetsEqual( - testC.listdir(), - [testC / testCopy.name, - testC / testFile.name, - testCopyOfLink]) - if hasattr(os, 'symlink'): - assert testCopyOfLink.islink() - assert testCopyOfLink.readlink() == testFile - - # Clean up. - testDir.rmtree() - assert not testDir.exists() - self.assertList(d.listdir(), []) - - def assertList(self, listing, expected): - assert sorted(listing) == sorted(expected) - - def test_patterns(self, tmpdir): - d = Path(tmpdir) - names = ['x.tmp', 'x.xtmp', 'x2g', 'x22', 'x.txt'] - dirs = [d, d / 'xdir', d / 'xdir.tmp', d / 'xdir.tmp' / 'xsubdir'] - - for e in dirs: - if not e.isdir(): - e.makedirs() - - for name in names: - (e / name).touch() - self.assertList(d.listdir('*.tmp'), [d / 'x.tmp', d / 'xdir.tmp']) - self.assertList(d.files('*.tmp'), [d / 'x.tmp']) - self.assertList(d.dirs('*.tmp'), [d / 'xdir.tmp']) - self.assertList(d.walk(), [e for e in dirs - if e != d] + [e / n for e in dirs - for n in names]) - self.assertList(d.walk('*.tmp'), - [e / 'x.tmp' for e in dirs] + [d / 'xdir.tmp']) - self.assertList(d.walkfiles('*.tmp'), [e / 'x.tmp' for e in dirs]) - self.assertList(d.walkdirs('*.tmp'), [d / 'xdir.tmp']) - - def test_unicode(self, tmpdir): - d = Path(tmpdir) - p = d / 'unicode.txt' - - def test(enc): - """ Test that path works with the specified encoding, - which must be capable of representing the entire range of - Unicode codepoints. - """ - - given = ( - 'Hello world\n' - '\u0d0a\u0a0d\u0d15\u0a15\r\n' - '\u0d0a\u0a0d\u0d15\u0a15\x85' - '\u0d0a\u0a0d\u0d15\u0a15\u2028' - '\r' - 'hanging' - ) - clean = ( - 'Hello world\n' - '\u0d0a\u0a0d\u0d15\u0a15\n' - '\u0d0a\u0a0d\u0d15\u0a15\n' - '\u0d0a\u0a0d\u0d15\u0a15\n' - '\n' - 'hanging' - ) - givenLines = [ - ('Hello world\n'), - ('\u0d0a\u0a0d\u0d15\u0a15\r\n'), - ('\u0d0a\u0a0d\u0d15\u0a15\x85'), - ('\u0d0a\u0a0d\u0d15\u0a15\u2028'), - ('\r'), - ('hanging')] - expectedLines = [ - ('Hello world\n'), - ('\u0d0a\u0a0d\u0d15\u0a15\n'), - ('\u0d0a\u0a0d\u0d15\u0a15\n'), - ('\u0d0a\u0a0d\u0d15\u0a15\n'), - ('\n'), - ('hanging')] - expectedLines2 = [ - ('Hello world'), - ('\u0d0a\u0a0d\u0d15\u0a15'), - ('\u0d0a\u0a0d\u0d15\u0a15'), - ('\u0d0a\u0a0d\u0d15\u0a15'), - (''), - ('hanging')] - - # write bytes manually to file - f = codecs.open(p, 'w', enc) - f.write(given) - f.close() - - # test all 3 path read-fully functions, including - # path.lines() in unicode mode. - assert p.bytes() == given.encode(enc) - assert p.text(enc) == clean - assert p.lines(enc) == expectedLines - assert p.lines(enc, retain=False) == expectedLines2 - - # If this is UTF-16, that's enough. - # The rest of these will unfortunately fail because append=True - # mode causes an extra BOM to be written in the middle of the file. - # UTF-16 is the only encoding that has this problem. - if enc == 'UTF-16': - return - - # Write Unicode to file using path.write_text(). - # This test doesn't work with a hanging line. - cleanNoHanging = clean + '\n' - - p.write_text(cleanNoHanging, enc) - p.write_text(cleanNoHanging, enc, append=True) - # Check the result. - expectedBytes = 2 * cleanNoHanging.replace('\n', - os.linesep).encode(enc) - expectedLinesNoHanging = expectedLines[:] - expectedLinesNoHanging[-1] += '\n' - assert p.bytes() == expectedBytes - assert p.text(enc) == 2 * cleanNoHanging - assert p.lines(enc) == 2 * expectedLinesNoHanging - assert p.lines(enc, retain=False) == 2 * expectedLines2 - - # Write Unicode to file using path.write_lines(). - # The output in the file should be exactly the same as last time. - p.write_lines(expectedLines, enc) - p.write_lines(expectedLines2, enc, append=True) - # Check the result. - assert p.bytes() == expectedBytes - - # Now: same test, but using various newline sequences. - # If linesep is being properly applied, these will be converted - # to the platform standard newline sequence. - p.write_lines(givenLines, enc) - p.write_lines(givenLines, enc, append=True) - # Check the result. - assert p.bytes() == expectedBytes - - # Same test, using newline sequences that are different - # from the platform default. - def testLinesep(eol): - p.write_lines(givenLines, enc, linesep=eol) - p.write_lines(givenLines, enc, linesep=eol, append=True) - expected = 2 * cleanNoHanging.replace('\n', eol).encode(enc) - assert p.bytes() == expected - - testLinesep('\n') - testLinesep('\r') - testLinesep('\r\n') - testLinesep('\x0d\x85') - - # Again, but with linesep=None. - p.write_lines(givenLines, enc, linesep=None) - p.write_lines(givenLines, enc, linesep=None, append=True) - # Check the result. - expectedBytes = 2 * given.encode(enc) - assert p.bytes() == expectedBytes - assert p.text(enc) == 2 * clean - expectedResultLines = expectedLines[:] - expectedResultLines[-1] += expectedLines[0] - expectedResultLines += expectedLines[1:] - assert p.lines(enc) == expectedResultLines - - test('UTF-8') - test('UTF-16BE') - test('UTF-16LE') - test('UTF-16') - - def test_chunks(self, tmpdir): - p = (TempDir() / 'test.txt').touch() - txt = "0123456789" - size = 5 - p.write_text(txt) - for i, chunk in enumerate(p.chunks(size)): - assert chunk == txt[i * size:i * size + size] - - assert i == len(txt) / size - 1 - - @pytest.mark.skipif( - not hasattr(os.path, 'samefile'), - reason="samefile not present", - ) - def test_samefile(self, tmpdir): - f1 = (TempDir() / '1.txt').touch() - f1.write_text('foo') - f2 = (TempDir() / '2.txt').touch() - f1.write_text('foo') - f3 = (TempDir() / '3.txt').touch() - f1.write_text('bar') - f4 = (TempDir() / '4.txt') - f1.copyfile(f4) - - assert os.path.samefile(f1, f2) == f1.samefile(f2) - assert os.path.samefile(f1, f3) == f1.samefile(f3) - assert os.path.samefile(f1, f4) == f1.samefile(f4) - assert os.path.samefile(f1, f1) == f1.samefile(f1) - - def test_rmtree_p(self, tmpdir): - d = Path(tmpdir) - sub = d / 'subfolder' - sub.mkdir() - (sub / 'afile').write_text('something') - sub.rmtree_p() - assert not sub.exists() - try: - sub.rmtree_p() - except OSError: - self.fail("Calling `rmtree_p` on non-existent directory " - "should not raise an exception.") - - def test_rmdir_p_exists(self, tmpdir): - """ - Invocation of rmdir_p on an existant directory should - remove the directory. - """ - d = Path(tmpdir) - sub = d / 'subfolder' - sub.mkdir() - sub.rmdir_p() - assert not sub.exists() - - def test_rmdir_p_nonexistent(self, tmpdir): - """ - A non-existent file should not raise an exception. - """ - d = Path(tmpdir) - sub = d / 'subfolder' - assert not sub.exists() - sub.rmdir_p() - - -class TestMergeTree: - @pytest.fixture(autouse=True) - def testing_structure(self, tmpdir): - self.test_dir = Path(tmpdir) - self.subdir_a = self.test_dir / 'A' - self.test_file = self.subdir_a / 'testfile.txt' - self.test_link = self.subdir_a / 'testlink.txt' - self.subdir_b = self.test_dir / 'B' - - self.subdir_a.mkdir() - self.subdir_b.mkdir() - - with open(self.test_file, 'w') as f: - f.write('x' * 10000) - - if hasattr(os, 'symlink'): - self.test_file.symlink(self.test_link) - else: - self.test_file.copy(self.test_link) - - def check_link(self): - target = Path(self.subdir_b / self.test_link.name) - check = target.islink if hasattr(os, 'symlink') else target.isfile - assert check() - - def test_with_nonexisting_dst_kwargs(self): - self.subdir_a.merge_tree(self.subdir_b, symlinks=True) - assert self.subdir_b.isdir() - expected = set(( - self.subdir_b / self.test_file.name, - self.subdir_b / self.test_link.name, - )) - assert set(self.subdir_b.listdir()) == expected - self.check_link() - - def test_with_nonexisting_dst_args(self): - self.subdir_a.merge_tree(self.subdir_b, True) - assert self.subdir_b.isdir() - expected = set(( - self.subdir_b / self.test_file.name, - self.subdir_b / self.test_link.name, - )) - assert set(self.subdir_b.listdir()) == expected - self.check_link() - - def test_with_existing_dst(self): - self.subdir_b.rmtree() - self.subdir_a.copytree(self.subdir_b, True) - - self.test_link.remove() - test_new = self.subdir_a / 'newfile.txt' - test_new.touch() - with open(self.test_file, 'w') as f: - f.write('x' * 5000) - - self.subdir_a.merge_tree(self.subdir_b, True) - - assert self.subdir_b.isdir() - expected = set(( - self.subdir_b / self.test_file.name, - self.subdir_b / self.test_link.name, - self.subdir_b / test_new.name, - )) - assert set(self.subdir_b.listdir()) == expected - self.check_link() - assert len(Path(self.subdir_b / self.test_file.name).bytes()) == 5000 - - def test_copytree_parameters(self): - """ - merge_tree should accept parameters to copytree, such as 'ignore' - """ - ignore = shutil.ignore_patterns('testlink*') - self.subdir_a.merge_tree(self.subdir_b, ignore=ignore) - - assert self.subdir_b.isdir() - assert self.subdir_b.listdir() == [self.subdir_b / self.test_file.name] - - def test_only_newer(self): - """ - merge_tree should accept a copy_function in which only - newer files are copied and older files do not overwrite - newer copies in the dest. - """ - target = self.subdir_b / 'testfile.txt' - target.write_text('this is newer') - self.subdir_a.merge_tree( - self.subdir_b, - copy_function=path.only_newer(shutil.copy2), - ) - assert target.text() == 'this is newer' - - -class TestChdir: - def test_chdir_or_cd(self, tmpdir): - """ tests the chdir or cd method """ - d = Path(str(tmpdir)) - cwd = d.getcwd() - - # ensure the cwd isn't our tempdir - assert str(d) != str(cwd) - # now, we're going to chdir to tempdir - d.chdir() - - # we now ensure that our cwd is the tempdir - assert str(d.getcwd()) == str(tmpdir) - # we're resetting our path - d = Path(cwd) - - # we ensure that our cwd is still set to tempdir - assert str(d.getcwd()) == str(tmpdir) - - # we're calling the alias cd method - d.cd() - # now, we ensure cwd isn'r tempdir - assert str(d.getcwd()) == str(cwd) - assert str(d.getcwd()) != str(tmpdir) - - -class TestSubclass: - - def test_subclass_produces_same_class(self): - """ - When operations are invoked on a subclass, they should produce another - instance of that subclass. - """ - class PathSubclass(Path): - pass - p = PathSubclass('/foo') - subdir = p / 'bar' - assert isinstance(subdir, PathSubclass) - - -class TestTempDir: - - def test_constructor(self): - """ - One should be able to readily construct a temporary directory - """ - d = TempDir() - assert isinstance(d, path.Path) - assert d.exists() - assert d.isdir() - d.rmdir() - assert not d.exists() - - def test_next_class(self): - """ - It should be possible to invoke operations on a TempDir and get - Path classes. - """ - d = TempDir() - sub = d / 'subdir' - assert isinstance(sub, path.Path) - d.rmdir() - - def test_context_manager(self): - """ - One should be able to use a TempDir object as a context, which will - clean up the contents after. - """ - d = TempDir() - res = d.__enter__() - assert res == path.Path(d) - (d / 'somefile.txt').touch() - assert not isinstance(d / 'somefile.txt', TempDir) - d.__exit__(None, None, None) - assert not d.exists() - - def test_context_manager_exception(self): - """ - The context manager will not clean up if an exception occurs. - """ - d = TempDir() - d.__enter__() - (d / 'somefile.txt').touch() - assert not isinstance(d / 'somefile.txt', TempDir) - d.__exit__(TypeError, TypeError('foo'), None) - assert d.exists() - - def test_context_manager_using_with(self): - """ - The context manager will allow using the with keyword and - provide a temporry directory that will be deleted after that. - """ - - with TempDir() as d: - assert d.isdir() - assert not d.isdir() - - -class TestUnicode: - @pytest.fixture(autouse=True) - def unicode_name_in_tmpdir(self, tmpdir): - # build a snowman (dir) in the temporary directory - Path(tmpdir).joinpath('☃').mkdir() - - def test_walkdirs_with_unicode_name(self, tmpdir): - for res in Path(tmpdir).walkdirs(): - pass - - -class TestPatternMatching: - def test_fnmatch_simple(self): - p = Path('FooBar') - assert p.fnmatch('Foo*') - assert p.fnmatch('Foo[ABC]ar') - - def test_fnmatch_custom_mod(self): - p = Path('FooBar') - p.module = ntpath - assert p.fnmatch('foobar') - assert p.fnmatch('FOO[ABC]AR') - - def test_fnmatch_custom_normcase(self): - def normcase(path): - return path.upper() - p = Path('FooBar') - assert p.fnmatch('foobar', normcase=normcase) - assert p.fnmatch('FOO[ABC]AR', normcase=normcase) - - def test_listdir_simple(self): - p = Path('.') - assert len(p.listdir()) == len(os.listdir('.')) - - def test_listdir_empty_pattern(self): - p = Path('.') - assert p.listdir('') == [] - - def test_listdir_patterns(self, tmpdir): - p = Path(tmpdir) - (p / 'sub').mkdir() - (p / 'File').touch() - assert p.listdir('s*') == [p / 'sub'] - assert len(p.listdir('*')) == 2 - - def test_listdir_custom_module(self, tmpdir): - """ - Listdir patterns should honor the case sensitivity of the path module - used by that Path class. - """ - always_unix = Path.using_module(posixpath) - p = always_unix(tmpdir) - (p / 'sub').mkdir() - (p / 'File').touch() - assert p.listdir('S*') == [] - - always_win = Path.using_module(ntpath) - p = always_win(tmpdir) - assert p.listdir('S*') == [p / 'sub'] - assert p.listdir('f*') == [p / 'File'] - - def test_listdir_case_insensitive(self, tmpdir): - """ - Listdir patterns should honor the case sensitivity of the path module - used by that Path class. - """ - p = Path(tmpdir) - (p / 'sub').mkdir() - (p / 'File').touch() - assert p.listdir(matchers.CaseInsensitive('S*')) == [p / 'sub'] - assert p.listdir(matchers.CaseInsensitive('f*')) == [p / 'File'] - assert p.files(matchers.CaseInsensitive('S*')) == [] - assert p.dirs(matchers.CaseInsensitive('f*')) == [] - - def test_walk_case_insensitive(self, tmpdir): - p = Path(tmpdir) - (p / 'sub1' / 'foo').makedirs_p() - (p / 'sub2' / 'foo').makedirs_p() - (p / 'sub1' / 'foo' / 'bar.Txt').touch() - (p / 'sub2' / 'foo' / 'bar.TXT').touch() - (p / 'sub2' / 'foo' / 'bar.txt.bz2').touch() - files = list(p.walkfiles(matchers.CaseInsensitive('*.txt'))) - assert len(files) == 2 - assert p / 'sub2' / 'foo' / 'bar.TXT' in files - assert p / 'sub1' / 'foo' / 'bar.Txt' in files - - -@pytest.mark.skipif( - sys.version_info < (2, 6), - reason="in_place requires io module in Python 2.6", -) -class TestInPlace: - reference_content = textwrap.dedent(""" - The quick brown fox jumped over the lazy dog. - """.lstrip()) - reversed_content = textwrap.dedent(""" - .god yzal eht revo depmuj xof nworb kciuq ehT - """.lstrip()) - alternate_content = textwrap.dedent(""" - Lorem ipsum dolor sit amet, consectetur adipisicing elit, - sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation - ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit - esse cillum dolore eu fugiat nulla pariatur. Excepteur - sint occaecat cupidatat non proident, sunt in culpa qui - officia deserunt mollit anim id est laborum. - """.lstrip()) - - @classmethod - def create_reference(cls, tmpdir): - p = Path(tmpdir) / 'document' - with p.open('w') as stream: - stream.write(cls.reference_content) - return p - - def test_line_by_line_rewrite(self, tmpdir): - doc = self.create_reference(tmpdir) - # reverse all the text in the document, line by line - with doc.in_place() as (reader, writer): - for line in reader: - r_line = ''.join(reversed(line.strip())) + '\n' - writer.write(r_line) - with doc.open() as stream: - data = stream.read() - assert data == self.reversed_content - - def test_exception_in_context(self, tmpdir): - doc = self.create_reference(tmpdir) - with pytest.raises(RuntimeError) as exc: - with doc.in_place() as (reader, writer): - writer.write(self.alternate_content) - raise RuntimeError("some error") - assert "some error" in str(exc) - with doc.open() as stream: - data = stream.read() - assert 'Lorem' not in data - assert 'lazy dog' in data - - -class TestSpecialPaths: - @pytest.fixture(autouse=True, scope='class') - def appdirs_installed(cls): - pytest.importorskip('appdirs') - - @pytest.fixture - def feign_linux(self, monkeypatch): - monkeypatch.setattr("platform.system", lambda: "Linux") - monkeypatch.setattr("sys.platform", "linux") - monkeypatch.setattr("os.pathsep", ":") - # remove any existing import of appdirs, as it sets up some - # state during import. - sys.modules.pop('appdirs') - - def test_basic_paths(self): - appdirs = importlib.import_module('appdirs') - - expected = appdirs.user_config_dir() - assert SpecialResolver(Path).user.config == expected - - expected = appdirs.site_config_dir() - assert SpecialResolver(Path).site.config == expected - - expected = appdirs.user_config_dir('My App', 'Me') - assert SpecialResolver(Path, 'My App', 'Me').user.config == expected - - def test_unix_paths(self, tmpdir, monkeypatch, feign_linux): - fake_config = tmpdir / '_config' - monkeypatch.setitem(os.environ, 'XDG_CONFIG_HOME', str(fake_config)) - expected = str(tmpdir / '_config') - assert SpecialResolver(Path).user.config == expected - - def test_unix_paths_fallback(self, tmpdir, monkeypatch, feign_linux): - "Without XDG_CONFIG_HOME set, ~/.config should be used." - fake_home = tmpdir / '_home' - monkeypatch.delitem(os.environ, 'XDG_CONFIG_HOME', raising=False) - monkeypatch.setitem(os.environ, 'HOME', str(fake_home)) - expected = Path('~/.config').expanduser() - assert SpecialResolver(Path).user.config == expected - - def test_property(self): - assert isinstance(Path.special().user.config, Path) - assert isinstance(Path.special().user.data, Path) - assert isinstance(Path.special().user.cache, Path) - - def test_other_parameters(self): - """ - Other parameters should be passed through to appdirs function. - """ - res = Path.special(version="1.0", multipath=True).site.config - assert isinstance(res, Path) - - def test_multipath(self, feign_linux, monkeypatch, tmpdir): - """ - If multipath is provided, on Linux return the XDG_CONFIG_DIRS - """ - fake_config_1 = str(tmpdir / '_config1') - fake_config_2 = str(tmpdir / '_config2') - config_dirs = os.pathsep.join([fake_config_1, fake_config_2]) - monkeypatch.setitem(os.environ, 'XDG_CONFIG_DIRS', config_dirs) - res = Path.special(multipath=True).site.config - assert isinstance(res, Multi) - assert fake_config_1 in res - assert fake_config_2 in res - assert '_config1' in str(res) - - def test_reused_SpecialResolver(self): - """ - Passing additional args and kwargs to SpecialResolver should be - passed through to each invocation of the function in appdirs. - """ - appdirs = importlib.import_module('appdirs') - - adp = SpecialResolver(Path, version="1.0") - res = adp.user.config - - expected = appdirs.user_config_dir(version="1.0") - assert res == expected - - -class TestMultiPath: - def test_for_class(self): - """ - Multi.for_class should return a subclass of the Path class provided. - """ - cls = Multi.for_class(Path) - assert issubclass(cls, Path) - assert issubclass(cls, Multi) - expected_name = 'Multi' + Path.__name__ - assert cls.__name__ == expected_name - - def test_detect_no_pathsep(self): - """ - If no pathsep is provided, multipath detect should return an instance - of the parent class with no Multi mix-in. - """ - path = Multi.for_class(Path).detect('/foo/bar') - assert isinstance(path, Path) - assert not isinstance(path, Multi) - - def test_detect_with_pathsep(self): - """ - If a pathsep appears in the input, detect should return an instance - of a Path with the Multi mix-in. - """ - inputs = '/foo/bar', '/baz/bing' - input = os.pathsep.join(inputs) - path = Multi.for_class(Path).detect(input) - - assert isinstance(path, Multi) - - def test_iteration(self): - """ - Iterating over a MultiPath should yield instances of the - parent class. - """ - inputs = '/foo/bar', '/baz/bing' - input = os.pathsep.join(inputs) - path = Multi.for_class(Path).detect(input) - - items = iter(path) - first = next(items) - assert first == '/foo/bar' - assert isinstance(first, Path) - assert not isinstance(first, Multi) - assert next(items) == '/baz/bing' - assert path == input - - -@pytest.mark.xfail('path.PY2', reason="Python 2 has no __future__") -def test_no_dependencies(): - """ - Path.py guarantees that the path module can be - transplanted into an environment without any dependencies. - """ - cmd = [ - sys.executable, - '-S', - '-c', 'import path', - ] - subprocess.check_call(cmd) - - -def test_version(): - """ - Under normal circumstances, path should present a - __version__. - """ - assert re.match(r'\d+\.\d+.*', path.__version__) diff --git a/libs/win/typing_extensions.py b/libs/win/typing_extensions.py new file mode 100644 index 00000000..ef42417c --- /dev/null +++ b/libs/win/typing_extensions.py @@ -0,0 +1,2209 @@ +import abc +import collections +import collections.abc +import functools +import operator +import sys +import types as _types +import typing + + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsIndex', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'get_overloads', + 'final', + 'get_args', + 'get_origin', + 'get_type_hints', + 'IntVar', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeGuard', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'Required', + 'NotRequired', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + +_marker = object() + + +def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not _is_unpack(t) + ): + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + +# On older versions of typing there is an internal class named "Final". +# 3.8+ +if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): + Final = typing.Final +# 3.7 +else: + class _FinalForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Final = _FinalForm('Final', + doc="""A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties.""") + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# 3.8+: +if hasattr(typing, 'Literal'): + Literal = typing.Literal +# 3.7: +else: + class _LiteralForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return typing._GenericAlias(self, parameters) + + Literal = _LiteralForm('Literal', + doc="""A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy # noqa + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + + +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +ContextManager = typing.ContextManager +AsyncContextManager = typing.AsyncContextManager +DefaultDict = typing.DefaultDict + +# 3.7.2+ +if hasattr(typing, 'OrderedDict'): + OrderedDict = typing.OrderedDict +# 3.7.0-3.7.2 +else: + OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) + +Counter = typing.Counter +ChainMap = typing.ChainMap +AsyncGenerator = typing.AsyncGenerator +NewType = typing.NewType +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +_PROTO_WHITELIST = ['Callable', 'Awaitable', + 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', + 'ContextManager', 'AsyncContextManager'] + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', '_gorg')): + attrs.add(attr) + return attrs + + +def _is_callable_members_only(cls): + return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) + + +def _maybe_adjust_parameters(cls): + """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__. + + The contents of this function are very similar + to logic found in typing.Generic.__init_subclass__ + on the CPython main branch. + """ + tvars = [] + if '__orig_bases__' in cls.__dict__: + tvars = typing._collect_type_vars(cls.__orig_bases__) + # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...] and/or Protocol[...]. + gvars = None + for base in cls.__orig_bases__: + if (isinstance(base, typing._GenericAlias) and + base.__origin__ in (typing.Generic, Protocol)): + # for error messages + the_base = base.__origin__.__name__ + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...]" + " and/or Protocol[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) + s_args = ', '.join(str(g) for g in gvars) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") + tvars = gvars + cls.__parameters__ = tuple(tvars) + + +# 3.8+ +if hasattr(typing, 'Protocol'): + Protocol = typing.Protocol +# 3.7 +else: + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(abc.ABCMeta): # noqa: B024 + # This metaclass is a bit unfortunate and exists only because of the lack + # of __instancehook__. + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(cls, '_is_protocol', False) or + _is_callable_members_only(cls)) and + issubclass(instance.__class__, cls)): + return True + if cls._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(cls, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(cls)): + return True + return super().__instancecheck__(instance) + + class Protocol(metaclass=_ProtocolMeta): + # There is quite a lot of overlapping code with typing.Generic. + # Unfortunately it is hard to avoid this while these live in two different + # modules. The duplicated code will be removed when Protocol is moved to typing. + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if cls is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can only be used as a base class") + return super().__new__(cls) + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple): + params = (params,) + if not params and cls is not typing.Tuple: + raise TypeError( + f"Parameter list to {cls.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) # noqa + if cls is Protocol: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, typing.TypeVar) for p in params): + i = 0 + while isinstance(params[i], typing.TypeVar): + i += 1 + raise TypeError( + "Parameters to Protocol[...] must all be type variables." + f" Parameter {i + 1} is {params[i]}") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Protocol[...] must all be unique") + else: + # Subscripting a regular Generic subclass. + _check_generic(cls, params, len(cls.__parameters__)) + return typing._GenericAlias(cls, params) + + def __init_subclass__(cls, *args, **kwargs): + if '__orig_bases__' in cls.__dict__: + error = typing.Generic in cls.__orig_bases__ + else: + error = typing.Generic in cls.__bases__ + if error: + raise TypeError("Cannot inherit from plain Generic") + _maybe_adjust_parameters(cls) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not getattr(cls, '_is_runtime_protocol', False): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if not _is_callable_members_only(cls): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # We have nothing more to do for non-protocols. + if not cls._is_protocol: + return + + # Check consistency of bases. + for base in cls.__bases__: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, _ProtocolMeta) and base._is_protocol): + raise TypeError('Protocols can only inherit from other' + f' protocols, got {repr(base)}') + cls.__init__ = _no_init + + +# 3.8+ +if hasattr(typing, 'runtime_checkable'): + runtime_checkable = typing.runtime_checkable +# 3.7 +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + return cls + + +# Exists for backwards compatibility. +runtime = runtime_checkable + + +# 3.8+ +if hasattr(typing, 'SupportsIndex'): + SupportsIndex = typing.SupportsIndex +# 3.7 +else: + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + +if hasattr(typing, "Required"): + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', + 'functools', + 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + def _dict_new(*args, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + return dict(*args, **kwargs) + + _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' + + def _typeddict_new(*args, total=True, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + if args: + typename, args = args[0], args[1:] # allow the "_typename" keyword be passed + elif '_typename' in kwargs: + typename = kwargs.pop('_typename') + import warnings + warnings.warn("Passing '_typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("TypedDict.__new__() missing 1 required positional " + "argument: '_typename'") + if args: + try: + fields, = args # allow the "_fields" keyword be passed + except ValueError: + raise TypeError('TypedDict.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + 'were given') + elif '_fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('_fields') + import warnings + warnings.warn("Passing '_fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + + if fields is None: + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(fields)} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(typename, (), ns, total=total) + + _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' + ' /, *, total=True, **kwargs)') + + class _TypedDictMeta(type): + def __init__(cls, name, bases, ns, total=True): + super().__init__(name, bases, ns) + + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new + # Don't insert typing.Generic into __bases__ here, + # or Generic.__init_subclass__ will raise TypeError + # in the super().__new__() call. + # Instead, monkey-patch __bases__ onto the class after it's been created. + tp_dict = super().__new__(cls, name, (dict,), ns) + + if any(issubclass(base, typing.Generic) for base in bases): + tp_dict.__bases__ = (typing.Generic, dict) + _maybe_adjust_parameters(tp_dict) + + annotations = {} + own_annotations = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + own_annotations = { + n: typing._type_check(tp, msg) for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + + for base in bases: + annotations.update(base.__dict__.get('__annotations__', {})) + required_keys.update(base.__dict__.get('__required_keys__', ())) + optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + annotations.update(own_annotations) + for annotation_key, annotation_type in own_annotations.items(): + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + annotation_origin = get_origin(annotation_type) + + if annotation_origin is Required: + required_keys.add(annotation_key) + elif annotation_origin is NotRequired: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) + TypedDict.__module__ = __name__ + TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports two additional equivalent forms:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + The class syntax is only supported in Python 3.6+, while two other + syntax forms work for Python 2.7 and 3.2+ + """ + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, tuple(_TYPEDDICT_TYPES)) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(__val, __typ): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return __val + + +if hasattr(typing, "Required"): + get_type_hints = typing.get_type_hints +else: + import functools + import types + + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return types.GenericAlias(t.__origin__, stripped_args) + if hasattr(types, "UnionType") and isinstance(t, types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.7-3.8 +else: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__,) + self.__metadata__ + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.7-3.9 +else: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias as _typing_GenericAlias + except ImportError: + _typing_GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__,) + tp.__metadata__ + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeAliasForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.7-3.8 +else: + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + TypeAlias = _TypeAliasForm('TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""") + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + + def __init__(self, default): + if isinstance(default, (tuple, list)): + self.__default__ = tuple((typing._type_check(d, "Default must be a type") + for d in default)) + elif default: + self.__default__ = typing._type_check(default, "Default must be a type") + else: + self.__default__ = None + + +# Add default and infer_variance parameters from PEP 696 and 695 +class TypeVar(typing.TypeVar, _DefaultMixin, _root=True): + """Type variable.""" + + __module__ = 'typing' + + def __init__(self, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=None, infer_variance=False): + super().__init__(name, *constraints, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + self.__infer_variance__ = infer_variance + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.7-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + +# 3.10+ +if hasattr(typing, 'ParamSpec'): + + # Add default Parameter - PEP 696 + class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True): + """Parameter specification variable.""" + + __module__ = 'typing' + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__(name, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +# 3.7-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__([self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.7-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + # Flag in 3.8. + _special = False + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + +# 3.7-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_TypeAliasForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.7-8 +else: + class _ConcatenateForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeGuardForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeGuardForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.7-3.8 +else: + class _TypeGuardForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) + + +# Vendored from cpython typing._SpecialFrom +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): + class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: + class _RequiredForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) + + +if hasattr(typing, "Unpack"): # 3.11+ + Unpack = typing.Unpack +elif sys.version_info[:2] >= (3, 9): + class _UnpackSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm( + 'Unpack', + doc="""A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +if hasattr(typing, "TypeVarTuple"): # 3.11+ + + # Add default Parameter - PEP 696 + class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True): + """Type variable tuple.""" + + def __init__(self, name, *, default=None): + super().__init__(name) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +else: + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=None): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): + reveal_type = typing.reveal_type +else: + def reveal_type(__obj: T) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr) + return __obj + + +if hasattr(typing, "assert_never"): + assert_never = typing.assert_never +else: + def assert_never(__arg: Never) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + raise AssertionError("Expected code to be unreachable") + + +if hasattr(typing, 'dataclass_transform'): + dataclass_transform = typing.dataclass_transform +else: + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): + override = typing.override +else: + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(__arg: _F) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: ... + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + See PEP 698 for details. + + """ + return __arg + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + typing._collect_type_vars = _collect_type_vars + typing._check_generic = _check_generic + + +# Backport typing.NamedTuple as it exists in Python 3.11. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +if sys.version_info >= (3, 11): + NamedTuple = typing.NamedTuple +else: + def _caller(): + try: + return sys._getframe(2).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): # For platforms without _getframe() + return None + + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + # The `_field_types` attribute was removed in 3.9; + # in earlier versions, it is the same as the `__annotations__` attribute + if sys.version_info < (3, 9): + nm_tpl._field_types = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + types = ns.get('__annotations__', {}) + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key in ns: + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields and key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + def NamedTuple(__typename, __fields=None, **kwargs): + if __fields is None: + __fields = kwargs.items() + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + return _make_nmtuple(__typename, __fields, module=_caller()) + + NamedTuple.__doc__ = typing.NamedTuple.__doc__ + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + # On 3.8+, alter the signature so that it matches typing.NamedTuple. + # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7, + # so just leave the signature as it is on 3.7. + if sys.version_info >= (3, 8): + NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)' + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + NamedTuple.__mro_entries__ = _namedtuple_mro_entries diff --git a/libs/win/zipp/__init__.py b/libs/win/zipp/__init__.py new file mode 100644 index 00000000..ad01e27e --- /dev/null +++ b/libs/win/zipp/__init__.py @@ -0,0 +1,381 @@ +import io +import posixpath +import zipfile +import itertools +import contextlib +import pathlib +import re +import fnmatch + +from .py310compat import text_encoding + + +__all__ = ['Path'] + + +def _parents(path): + """ + Given a path with elements separated by + posixpath.sep, generate all parents of that path. + + >>> list(_parents('b/d')) + ['b'] + >>> list(_parents('/b/d/')) + ['/b'] + >>> list(_parents('b/d/f/')) + ['b/d', 'b'] + >>> list(_parents('b')) + [] + >>> list(_parents('')) + [] + """ + return itertools.islice(_ancestry(path), 1, None) + + +def _ancestry(path): + """ + Given a path with elements separated by + posixpath.sep, generate all elements of that path + + >>> list(_ancestry('b/d')) + ['b/d', 'b'] + >>> list(_ancestry('/b/d/')) + ['/b/d', '/b'] + >>> list(_ancestry('b/d/f/')) + ['b/d/f', 'b/d', 'b'] + >>> list(_ancestry('b')) + ['b'] + >>> list(_ancestry('')) + [] + """ + path = path.rstrip(posixpath.sep) + while path and path != posixpath.sep: + yield path + path, tail = posixpath.split(path) + + +_dedupe = dict.fromkeys +"""Deduplicate an iterable in original order""" + + +def _difference(minuend, subtrahend): + """ + Return items in minuend not in subtrahend, retaining order + with O(1) lookup. + """ + return itertools.filterfalse(set(subtrahend).__contains__, minuend) + + +class InitializedState: + """ + Mix-in to save the initialization state for pickling. + """ + + def __init__(self, *args, **kwargs): + self.__args = args + self.__kwargs = kwargs + super().__init__(*args, **kwargs) + + def __getstate__(self): + return self.__args, self.__kwargs + + def __setstate__(self, state): + args, kwargs = state + super().__init__(*args, **kwargs) + + +class CompleteDirs(InitializedState, zipfile.ZipFile): + """ + A ZipFile subclass that ensures that implied directories + are always included in the namelist. + """ + + @staticmethod + def _implied_dirs(names): + parents = itertools.chain.from_iterable(map(_parents, names)) + as_dirs = (p + posixpath.sep for p in parents) + return _dedupe(_difference(as_dirs, names)) + + def namelist(self): + names = super(CompleteDirs, self).namelist() + return names + list(self._implied_dirs(names)) + + def _name_set(self): + return set(self.namelist()) + + def resolve_dir(self, name): + """ + If the name represents a directory, return that name + as a directory (with the trailing slash). + """ + names = self._name_set() + dirname = name + '/' + dir_match = name not in names and dirname in names + return dirname if dir_match else name + + @classmethod + def make(cls, source): + """ + Given a source (filename or zipfile), return an + appropriate CompleteDirs subclass. + """ + if isinstance(source, CompleteDirs): + return source + + if not isinstance(source, zipfile.ZipFile): + return cls(source) + + # Only allow for FastLookup when supplied zipfile is read-only + if 'r' not in source.mode: + cls = CompleteDirs + + source.__class__ = cls + return source + + +class FastLookup(CompleteDirs): + """ + ZipFile subclass to ensure implicit + dirs exist and are resolved rapidly. + """ + + def namelist(self): + with contextlib.suppress(AttributeError): + return self.__names + self.__names = super(FastLookup, self).namelist() + return self.__names + + def _name_set(self): + with contextlib.suppress(AttributeError): + return self.__lookup + self.__lookup = super(FastLookup, self)._name_set() + return self.__lookup + + +class Path: + """ + A pathlib-compatible interface for zip files. + + Consider a zip file with this structure:: + + . + ├── a.txt + └── b + ├── c.txt + └── d + └── e.txt + + >>> data = io.BytesIO() + >>> zf = zipfile.ZipFile(data, 'w') + >>> zf.writestr('a.txt', 'content of a') + >>> zf.writestr('b/c.txt', 'content of c') + >>> zf.writestr('b/d/e.txt', 'content of e') + >>> zf.filename = 'mem/abcde.zip' + + Path accepts the zipfile object itself or a filename + + >>> root = Path(zf) + + From there, several path operations are available. + + Directory iteration (including the zip file itself): + + >>> a, b = root.iterdir() + >>> a + Path('mem/abcde.zip', 'a.txt') + >>> b + Path('mem/abcde.zip', 'b/') + + name property: + + >>> b.name + 'b' + + join with divide operator: + + >>> c = b / 'c.txt' + >>> c + Path('mem/abcde.zip', 'b/c.txt') + >>> c.name + 'c.txt' + + Read text: + + >>> c.read_text() + 'content of c' + + existence: + + >>> c.exists() + True + >>> (b / 'missing.txt').exists() + False + + Coercion to string: + + >>> import os + >>> str(c).replace(os.sep, posixpath.sep) + 'mem/abcde.zip/b/c.txt' + + At the root, ``name``, ``filename``, and ``parent`` + resolve to the zipfile. Note these attributes are not + valid and will raise a ``ValueError`` if the zipfile + has no filename. + + >>> root.name + 'abcde.zip' + >>> str(root.filename).replace(os.sep, posixpath.sep) + 'mem/abcde.zip' + >>> str(root.parent) + 'mem' + """ + + __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" + + def __init__(self, root, at=""): + """ + Construct a Path from a ZipFile or filename. + + Note: When the source is an existing ZipFile object, + its type (__class__) will be mutated to a + specialized type. If the caller wishes to retain the + original type, the caller should either create a + separate ZipFile object or pass a filename. + """ + self.root = FastLookup.make(root) + self.at = at + + def __eq__(self, other): + """ + >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' + False + """ + if self.__class__ is not other.__class__: + return NotImplemented + return (self.root, self.at) == (other.root, other.at) + + def __hash__(self): + return hash((self.root, self.at)) + + def open(self, mode='r', *args, pwd=None, **kwargs): + """ + Open this entry as text or binary following the semantics + of ``pathlib.Path.open()`` by passing arguments through + to io.TextIOWrapper(). + """ + if self.is_dir(): + raise IsADirectoryError(self) + zip_mode = mode[0] + if not self.exists() and zip_mode == 'r': + raise FileNotFoundError(self) + stream = self.root.open(self.at, zip_mode, pwd=pwd) + if 'b' in mode: + if args or kwargs: + raise ValueError("encoding args invalid for binary operation") + return stream + else: + kwargs["encoding"] = text_encoding(kwargs.get("encoding")) + return io.TextIOWrapper(stream, *args, **kwargs) + + @property + def name(self): + return pathlib.Path(self.at).name or self.filename.name + + @property + def suffix(self): + return pathlib.Path(self.at).suffix or self.filename.suffix + + @property + def suffixes(self): + return pathlib.Path(self.at).suffixes or self.filename.suffixes + + @property + def stem(self): + return pathlib.Path(self.at).stem or self.filename.stem + + @property + def filename(self): + return pathlib.Path(self.root.filename).joinpath(self.at) + + def read_text(self, *args, **kwargs): + kwargs["encoding"] = text_encoding(kwargs.get("encoding")) + with self.open('r', *args, **kwargs) as strm: + return strm.read() + + def read_bytes(self): + with self.open('rb') as strm: + return strm.read() + + def _is_child(self, path): + return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") + + def _next(self, at): + return self.__class__(self.root, at) + + def is_dir(self): + return not self.at or self.at.endswith("/") + + def is_file(self): + return self.exists() and not self.is_dir() + + def exists(self): + return self.at in self.root._name_set() + + def iterdir(self): + if not self.is_dir(): + raise ValueError("Can't listdir a file") + subs = map(self._next, self.root.namelist()) + return filter(self._is_child, subs) + + def match(self, path_pattern): + return pathlib.Path(self.at).match(path_pattern) + + def is_symlink(self): + """ + Return whether this path is a symlink. Always false (python/cpython#82102). + """ + return False + + def _descendants(self): + for child in self.iterdir(): + yield child + if child.is_dir(): + yield from child._descendants() + + def glob(self, pattern): + if not pattern: + raise ValueError("Unacceptable pattern: {!r}".format(pattern)) + + matches = re.compile(fnmatch.translate(pattern)).fullmatch + return ( + child + for child in self._descendants() + if matches(str(child.relative_to(self))) + ) + + def rglob(self, pattern): + return self.glob(f'**/{pattern}') + + def relative_to(self, other, *extra): + return posixpath.relpath(str(self), str(other.joinpath(*extra))) + + def __str__(self): + return posixpath.join(self.root.filename, self.at) + + def __repr__(self): + return self.__repr.format(self=self) + + def joinpath(self, *other): + next = posixpath.join(self.at, *other) + return self._next(self.root.resolve_dir(next)) + + __truediv__ = joinpath + + @property + def parent(self): + if not self.at: + return self.filename.parent + parent_at = posixpath.dirname(self.at.rstrip('/')) + if parent_at: + parent_at += '/' + return self._next(parent_at) diff --git a/libs/win/zipp/py310compat.py b/libs/win/zipp/py310compat.py new file mode 100644 index 00000000..8244124c --- /dev/null +++ b/libs/win/zipp/py310compat.py @@ -0,0 +1,12 @@ +import sys +import io + + +te_impl = 'lambda encoding, stacklevel=2, /: encoding' +te_impl_37 = te_impl.replace(', /', '') +_text_encoding = eval(te_impl) if sys.version_info > (3, 8) else eval(te_impl_37) + + +text_encoding = ( + io.text_encoding if sys.version_info > (3, 10) else _text_encoding # type: ignore +) From aed4e9261cb7023237b8eb32e5fdc00664485d57 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 06:10:54 -0500 Subject: [PATCH 28/47] Update vendored configobj to 5.0.6 Updates vendored six to 1.16.0 --- libs/common/configobj.py | 2483 ++++++++++++++++++++++++++++++++++++++ libs/common/six.py | 104 +- 2 files changed, 2558 insertions(+), 29 deletions(-) create mode 100644 libs/common/configobj.py diff --git a/libs/common/configobj.py b/libs/common/configobj.py new file mode 100644 index 00000000..ba886e86 --- /dev/null +++ b/libs/common/configobj.py @@ -0,0 +1,2483 @@ +# configobj.py +# A config file reader/writer that supports nested sections in config files. +# Copyright (C) 2005-2014: +# (name) : (email) +# Michael Foord: fuzzyman AT voidspace DOT org DOT uk +# Nicola Larosa: nico AT tekNico DOT net +# Rob Dennis: rdennis AT gmail DOT com +# Eli Courtwright: eli AT courtwright DOT org + +# This software is licensed under the terms of the BSD license. +# http://opensource.org/licenses/BSD-3-Clause + +# ConfigObj 5 - main repository for documentation and issue tracking: +# https://github.com/DiffSK/configobj + +import os +import re +import sys + +from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE + +import six +from _version import __version__ + +# imported lazily to avoid startup performance hit if it isn't used +compiler = None + +# A dictionary mapping BOM to +# the encoding to decode with, and what to set the +# encoding attribute to. +BOMS = { + BOM_UTF8: ('utf_8', None), + BOM_UTF16_BE: ('utf16_be', 'utf_16'), + BOM_UTF16_LE: ('utf16_le', 'utf_16'), + BOM_UTF16: ('utf_16', 'utf_16'), + } +# All legal variants of the BOM codecs. +# TODO: the list of aliases is not meant to be exhaustive, is there a +# better way ? +BOM_LIST = { + 'utf_16': 'utf_16', + 'u16': 'utf_16', + 'utf16': 'utf_16', + 'utf-16': 'utf_16', + 'utf16_be': 'utf16_be', + 'utf_16_be': 'utf16_be', + 'utf-16be': 'utf16_be', + 'utf16_le': 'utf16_le', + 'utf_16_le': 'utf16_le', + 'utf-16le': 'utf16_le', + 'utf_8': 'utf_8', + 'u8': 'utf_8', + 'utf': 'utf_8', + 'utf8': 'utf_8', + 'utf-8': 'utf_8', + } + +# Map of encodings to the BOM to write. +BOM_SET = { + 'utf_8': BOM_UTF8, + 'utf_16': BOM_UTF16, + 'utf16_be': BOM_UTF16_BE, + 'utf16_le': BOM_UTF16_LE, + None: BOM_UTF8 + } + + +def match_utf8(encoding): + return BOM_LIST.get(encoding.lower()) == 'utf_8' + + +# Quote strings used for writing values +squot = "'%s'" +dquot = '"%s"' +noquot = "%s" +wspace_plus = ' \r\n\v\t\'"' +tsquot = '"""%s"""' +tdquot = "'''%s'''" + +# Sentinel for use in getattr calls to replace hasattr +MISSING = object() + +__all__ = ( + 'DEFAULT_INDENT_TYPE', + 'DEFAULT_INTERPOLATION', + 'ConfigObjError', + 'NestingError', + 'ParseError', + 'DuplicateError', + 'ConfigspecError', + 'ConfigObj', + 'SimpleVal', + 'InterpolationError', + 'InterpolationLoopError', + 'MissingInterpolationOption', + 'RepeatSectionError', + 'ReloadError', + 'UnreprError', + 'UnknownType', + 'flatten_errors', + 'get_extra_values' +) + +DEFAULT_INTERPOLATION = 'configparser' +DEFAULT_INDENT_TYPE = ' ' +MAX_INTERPOL_DEPTH = 10 + +OPTION_DEFAULTS = { + 'interpolation': True, + 'raise_errors': False, + 'list_values': True, + 'create_empty': False, + 'file_error': False, + 'configspec': None, + 'stringify': True, + # option may be set to one of ('', ' ', '\t') + 'indent_type': None, + 'encoding': None, + 'default_encoding': None, + 'unrepr': False, + 'write_empty_values': False, +} + +# this could be replaced if six is used for compatibility, or there are no +# more assertions about items being a string + + +def getObj(s): + global compiler + if compiler is None: + import compiler + s = "a=" + s + p = compiler.parse(s) + return p.getChildren()[1].getChildren()[0].getChildren()[1] + + +class UnknownType(Exception): + pass + + +class Builder(object): + + def build(self, o): + if m is None: + raise UnknownType(o.__class__.__name__) + return m(o) + + def build_List(self, o): + return list(map(self.build, o.getChildren())) + + def build_Const(self, o): + return o.value + + def build_Dict(self, o): + d = {} + i = iter(map(self.build, o.getChildren())) + for el in i: + d[el] = next(i) + return d + + def build_Tuple(self, o): + return tuple(self.build_List(o)) + + def build_Name(self, o): + if o.name == 'None': + return None + if o.name == 'True': + return True + if o.name == 'False': + return False + + # An undefined Name + raise UnknownType('Undefined Name') + + def build_Add(self, o): + real, imag = list(map(self.build_Const, o.getChildren())) + try: + real = float(real) + except TypeError: + raise UnknownType('Add') + if not isinstance(imag, complex) or imag.real != 0.0: + raise UnknownType('Add') + return real+imag + + def build_Getattr(self, o): + parent = self.build(o.expr) + return getattr(parent, o.attrname) + + def build_UnarySub(self, o): + return -self.build_Const(o.getChildren()[0]) + + def build_UnaryAdd(self, o): + return self.build_Const(o.getChildren()[0]) + + +_builder = Builder() + + +def unrepr(s): + if not s: + return s + + # this is supposed to be safe + import ast + return ast.literal_eval(s) + + +class ConfigObjError(SyntaxError): + """ + This is the base class for all errors that ConfigObj raises. + It is a subclass of SyntaxError. + """ + def __init__(self, message='', line_number=None, line=''): + self.line = line + self.line_number = line_number + SyntaxError.__init__(self, message) + + +class NestingError(ConfigObjError): + """ + This error indicates a level of nesting that doesn't match. + """ + + +class ParseError(ConfigObjError): + """ + This error indicates that a line is badly written. + It is neither a valid ``key = value`` line, + nor a valid section marker line. + """ + + +class ReloadError(IOError): + """ + A 'reload' operation failed. + This exception is a subclass of ``IOError``. + """ + def __init__(self): + IOError.__init__(self, 'reload failed, filename is not set.') + + +class DuplicateError(ConfigObjError): + """ + The keyword or section specified already exists. + """ + + +class ConfigspecError(ConfigObjError): + """ + An error occured whilst parsing a configspec. + """ + + +class InterpolationError(ConfigObjError): + """Base class for the two interpolation errors.""" + + +class InterpolationLoopError(InterpolationError): + """Maximum interpolation depth exceeded in string interpolation.""" + + def __init__(self, option): + InterpolationError.__init__( + self, + 'interpolation loop detected in value "%s".' % option) + + +class RepeatSectionError(ConfigObjError): + """ + This error indicates additional sections in a section with a + ``__many__`` (repeated) section. + """ + + +class MissingInterpolationOption(InterpolationError): + """A value specified for interpolation was missing.""" + def __init__(self, option): + msg = 'missing option "%s" in interpolation.' % option + InterpolationError.__init__(self, msg) + + +class UnreprError(ConfigObjError): + """An error parsing in unrepr mode.""" + + + +class InterpolationEngine(object): + """ + A helper class to help perform string interpolation. + + This class is an abstract base class; its descendants perform + the actual work. + """ + + # compiled regexp to use in self.interpolate() + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + _cookie = '%' + + def __init__(self, section): + # the Section instance that "owns" this engine + self.section = section + + + def interpolate(self, key, value): + # short-cut + if not self._cookie in value: + return value + + def recursive_interpolate(key, value, section, backtrail): + """The function that does the actual work. + + ``value``: the string we're trying to interpolate. + ``section``: the section in which that string was found + ``backtrail``: a dict to keep track of where we've been, + to detect and prevent infinite recursion loops + + This is similar to a depth-first-search algorithm. + """ + # Have we been here already? + if (key, section.name) in backtrail: + # Yes - infinite loop detected + raise InterpolationLoopError(key) + # Place a marker on our backtrail so we won't come back here again + backtrail[(key, section.name)] = 1 + + # Now start the actual work + match = self._KEYCRE.search(value) + while match: + # The actual parsing of the match is implementation-dependent, + # so delegate to our helper function + k, v, s = self._parse_match(match) + if k is None: + # That's the signal that no further interpolation is needed + replacement = v + else: + # Further interpolation may be needed to obtain final value + replacement = recursive_interpolate(k, v, s, backtrail) + # Replace the matched string with its final value + start, end = match.span() + value = ''.join((value[:start], replacement, value[end:])) + new_search_start = start + len(replacement) + # Pick up the next interpolation key, if any, for next time + # through the while loop + match = self._KEYCRE.search(value, new_search_start) + + # Now safe to come back here again; remove marker from backtrail + del backtrail[(key, section.name)] + + return value + + # Back in interpolate(), all we have to do is kick off the recursive + # function with appropriate starting values + value = recursive_interpolate(key, value, self.section, {}) + return value + + + def _fetch(self, key): + """Helper function to fetch values from owning section. + + Returns a 2-tuple: the value, and the section where it was found. + """ + # switch off interpolation before we try and fetch anything ! + save_interp = self.section.main.interpolation + self.section.main.interpolation = False + + # Start at section that "owns" this InterpolationEngine + current_section = self.section + while True: + # try the current section first + val = current_section.get(key) + if val is not None and not isinstance(val, Section): + break + # try "DEFAULT" next + val = current_section.get('DEFAULT', {}).get(key) + if val is not None and not isinstance(val, Section): + break + # move up to parent and try again + # top-level's parent is itself + if current_section.parent is current_section: + # reached top level, time to give up + break + current_section = current_section.parent + + # restore interpolation to previous value before returning + self.section.main.interpolation = save_interp + if val is None: + raise MissingInterpolationOption(key) + return val, current_section + + + def _parse_match(self, match): + """Implementation-dependent helper function. + + Will be passed a match object corresponding to the interpolation + key we just found (e.g., "%(foo)s" or "$foo"). Should look up that + key in the appropriate config file section (using the ``_fetch()`` + helper function) and return a 3-tuple: (key, value, section) + + ``key`` is the name of the key we're looking for + ``value`` is the value found for that key + ``section`` is a reference to the section where it was found + + ``key`` and ``section`` should be None if no further + interpolation should be performed on the resulting value + (e.g., if we interpolated "$$" and returned "$"). + """ + raise NotImplementedError() + + + +class ConfigParserInterpolation(InterpolationEngine): + """Behaves like ConfigParser.""" + _cookie = '%' + _KEYCRE = re.compile(r"%\(([^)]*)\)s") + + def _parse_match(self, match): + key = match.group(1) + value, section = self._fetch(key) + return key, value, section + + + +class TemplateInterpolation(InterpolationEngine): + """Behaves like string.Template.""" + _cookie = '$' + _delimiter = '$' + _KEYCRE = re.compile(r""" + \$(?: + (?P\$) | # Two $ signs + (?P[_a-z][_a-z0-9]*) | # $name format + {(?P[^}]*)} # ${name} format + ) + """, re.IGNORECASE | re.VERBOSE) + + def _parse_match(self, match): + # Valid name (in or out of braces): fetch value from section + key = match.group('named') or match.group('braced') + if key is not None: + value, section = self._fetch(key) + return key, value, section + # Escaped delimiter (e.g., $$): return single delimiter + if match.group('escaped') is not None: + # Return None for key and section to indicate it's time to stop + return None, self._delimiter, None + # Anything else: ignore completely, just return it unchanged + return None, match.group(), None + + +interpolation_engines = { + 'configparser': ConfigParserInterpolation, + 'template': TemplateInterpolation, +} + + +def __newobj__(cls, *args): + # Hack for pickle + return cls.__new__(cls, *args) + +class Section(dict): + """ + A dictionary-like object that represents a section in a config file. + + It does string interpolation if the 'interpolation' attribute + of the 'main' object is set to True. + + Interpolation is tried first from this object, then from the 'DEFAULT' + section of this object, next from the parent and its 'DEFAULT' section, + and so on until the main object is reached. + + A Section will behave like an ordered dictionary - following the + order of the ``scalars`` and ``sections`` attributes. + You can use this to change the order of members. + + Iteration follows the order: scalars, then sections. + """ + + + def __setstate__(self, state): + dict.update(self, state[0]) + self.__dict__.update(state[1]) + + def __reduce__(self): + state = (dict(self), self.__dict__) + return (__newobj__, (self.__class__,), state) + + + def __init__(self, parent, depth, main, indict=None, name=None): + """ + * parent is the section above + * depth is the depth level of this section + * main is the main ConfigObj + * indict is a dictionary to initialise the section with + """ + if indict is None: + indict = {} + dict.__init__(self) + # used for nesting level *and* interpolation + self.parent = parent + # used for the interpolation attribute + self.main = main + # level of nesting depth of this Section + self.depth = depth + # purely for information + self.name = name + # + self._initialise() + # we do this explicitly so that __setitem__ is used properly + # (rather than just passing to ``dict.__init__``) + for entry, value in indict.items(): + self[entry] = value + + + def _initialise(self): + # the sequence of scalar values in this Section + self.scalars = [] + # the sequence of sections in this Section + self.sections = [] + # for comments :-) + self.comments = {} + self.inline_comments = {} + # the configspec + self.configspec = None + # for defaults + self.defaults = [] + self.default_values = {} + self.extra_values = [] + self._created = False + + + def _interpolate(self, key, value): + try: + # do we already have an interpolation engine? + engine = self._interpolation_engine + except AttributeError: + # not yet: first time running _interpolate(), so pick the engine + name = self.main.interpolation + if name == True: # note that "if name:" would be incorrect here + # backwards-compatibility: interpolation=True means use default + name = DEFAULT_INTERPOLATION + name = name.lower() # so that "Template", "template", etc. all work + class_ = interpolation_engines.get(name, None) + if class_ is None: + # invalid value for self.main.interpolation + self.main.interpolation = False + return value + else: + # save reference to engine so we don't have to do this again + engine = self._interpolation_engine = class_(self) + # let the engine do the actual work + return engine.interpolate(key, value) + + + def __getitem__(self, key): + """Fetch the item and do string interpolation.""" + val = dict.__getitem__(self, key) + if self.main.interpolation: + if isinstance(val, six.string_types): + return self._interpolate(key, val) + if isinstance(val, list): + def _check(entry): + if isinstance(entry, six.string_types): + return self._interpolate(key, entry) + return entry + new = [_check(entry) for entry in val] + if new != val: + return new + return val + + + def __setitem__(self, key, value, unrepr=False): + """ + Correctly set a value. + + Making dictionary values Section instances. + (We have to special case 'Section' instances - which are also dicts) + + Keys must be strings. + Values need only be strings (or lists of strings) if + ``main.stringify`` is set. + + ``unrepr`` must be set when setting a value to a dictionary, without + creating a new sub-section. + """ + if not isinstance(key, six.string_types): + raise ValueError('The key "%s" is not a string.' % key) + + # add the comment + if key not in self.comments: + self.comments[key] = [] + self.inline_comments[key] = '' + # remove the entry from defaults + if key in self.defaults: + self.defaults.remove(key) + # + if isinstance(value, Section): + if key not in self: + self.sections.append(key) + dict.__setitem__(self, key, value) + elif isinstance(value, dict) and not unrepr: + # First create the new depth level, + # then create the section + if key not in self: + self.sections.append(key) + new_depth = self.depth + 1 + dict.__setitem__( + self, + key, + Section( + self, + new_depth, + self.main, + indict=value, + name=key)) + else: + if key not in self: + self.scalars.append(key) + if not self.main.stringify: + if isinstance(value, six.string_types): + pass + elif isinstance(value, (list, tuple)): + for entry in value: + if not isinstance(entry, six.string_types): + raise TypeError('Value is not a string "%s".' % entry) + else: + raise TypeError('Value is not a string "%s".' % value) + dict.__setitem__(self, key, value) + + + def __delitem__(self, key): + """Remove items from the sequence when deleting.""" + dict. __delitem__(self, key) + if key in self.scalars: + self.scalars.remove(key) + else: + self.sections.remove(key) + del self.comments[key] + del self.inline_comments[key] + + + def get(self, key, default=None): + """A version of ``get`` that doesn't bypass string interpolation.""" + try: + return self[key] + except KeyError: + return default + + + def update(self, indict): + """ + A version of update that uses our ``__setitem__``. + """ + for entry in indict: + self[entry] = indict[entry] + + + def pop(self, key, default=MISSING): + """ + 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised' + """ + try: + val = self[key] + except KeyError: + if default is MISSING: + raise + val = default + else: + del self[key] + return val + + + def popitem(self): + """Pops the first (key,val)""" + sequence = (self.scalars + self.sections) + if not sequence: + raise KeyError(": 'popitem(): dictionary is empty'") + key = sequence[0] + val = self[key] + del self[key] + return key, val + + + def clear(self): + """ + A version of clear that also affects scalars/sections + Also clears comments and configspec. + + Leaves other attributes alone : + depth/main/parent are not affected + """ + dict.clear(self) + self.scalars = [] + self.sections = [] + self.comments = {} + self.inline_comments = {} + self.configspec = None + self.defaults = [] + self.extra_values = [] + + + def setdefault(self, key, default=None): + """A version of setdefault that sets sequence if appropriate.""" + try: + return self[key] + except KeyError: + self[key] = default + return self[key] + + + def items(self): + """D.items() -> list of D's (key, value) pairs, as 2-tuples""" + return list(zip((self.scalars + self.sections), list(self.values()))) + + + def keys(self): + """D.keys() -> list of D's keys""" + return (self.scalars + self.sections) + + + def values(self): + """D.values() -> list of D's values""" + return [self[key] for key in (self.scalars + self.sections)] + + + def iteritems(self): + """D.iteritems() -> an iterator over the (key, value) items of D""" + return iter(list(self.items())) + + + def iterkeys(self): + """D.iterkeys() -> an iterator over the keys of D""" + return iter((self.scalars + self.sections)) + + __iter__ = iterkeys + + + def itervalues(self): + """D.itervalues() -> an iterator over the values of D""" + return iter(list(self.values())) + + + def __repr__(self): + """x.__repr__() <==> repr(x)""" + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) + for key in (self.scalars + self.sections)]) + + __str__ = __repr__ + __str__.__doc__ = "x.__str__() <==> str(x)" + + + # Extra methods - not in a normal dictionary + + def dict(self): + """ + Return a deepcopy of self as a dictionary. + + All members that are ``Section`` instances are recursively turned to + ordinary dictionaries - by calling their ``dict`` method. + + >>> n = a.dict() + >>> n == a + 1 + >>> n is a + 0 + """ + newdict = {} + for entry in self: + this_entry = self[entry] + if isinstance(this_entry, Section): + this_entry = this_entry.dict() + elif isinstance(this_entry, list): + # create a copy rather than a reference + this_entry = list(this_entry) + elif isinstance(this_entry, tuple): + # create a copy rather than a reference + this_entry = tuple(this_entry) + newdict[entry] = this_entry + return newdict + + + def merge(self, indict): + """ + A recursive update - useful for merging config files. + + >>> a = '''[section1] + ... option1 = True + ... [[subsection]] + ... more_options = False + ... # end of file'''.splitlines() + >>> b = '''# File is user.ini + ... [section1] + ... option1 = False + ... # end of file'''.splitlines() + >>> c1 = ConfigObj(b) + >>> c2 = ConfigObj(a) + >>> c2.merge(c1) + >>> c2 + ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) + """ + for key, val in list(indict.items()): + if (key in self and isinstance(self[key], dict) and + isinstance(val, dict)): + self[key].merge(val) + else: + self[key] = val + + + def rename(self, oldkey, newkey): + """ + Change a keyname to another, without changing position in sequence. + + Implemented so that transformations can be made on keys, + as well as on values. (used by encode and decode) + + Also renames comments. + """ + if oldkey in self.scalars: + the_list = self.scalars + elif oldkey in self.sections: + the_list = self.sections + else: + raise KeyError('Key "%s" not found.' % oldkey) + pos = the_list.index(oldkey) + # + val = self[oldkey] + dict.__delitem__(self, oldkey) + dict.__setitem__(self, newkey, val) + the_list.remove(oldkey) + the_list.insert(pos, newkey) + comm = self.comments[oldkey] + inline_comment = self.inline_comments[oldkey] + del self.comments[oldkey] + del self.inline_comments[oldkey] + self.comments[newkey] = comm + self.inline_comments[newkey] = inline_comment + + + def walk(self, function, raise_errors=True, + call_on_sections=False, **keywargs): + """ + Walk every member and call a function on the keyword and value. + + Return a dictionary of the return values + + If the function raises an exception, raise the errror + unless ``raise_errors=False``, in which case set the return value to + ``False``. + + Any unrecognised keyword arguments you pass to walk, will be pased on + to the function you pass in. + + Note: if ``call_on_sections`` is ``True`` then - on encountering a + subsection, *first* the function is called for the *whole* subsection, + and then recurses into it's members. This means your function must be + able to handle strings, dictionaries and lists. This allows you + to change the key of subsections as well as for ordinary members. The + return value when called on the whole subsection has to be discarded. + + See the encode and decode methods for examples, including functions. + + .. admonition:: caution + + You can use ``walk`` to transform the names of members of a section + but you mustn't add or delete members. + + >>> config = '''[XXXXsection] + ... XXXXkey = XXXXvalue'''.splitlines() + >>> cfg = ConfigObj(config) + >>> cfg + ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) + >>> def transform(section, key): + ... val = section[key] + ... newkey = key.replace('XXXX', 'CLIENT1') + ... section.rename(key, newkey) + ... if isinstance(val, (tuple, list, dict)): + ... pass + ... else: + ... val = val.replace('XXXX', 'CLIENT1') + ... section[newkey] = val + >>> cfg.walk(transform, call_on_sections=True) + {'CLIENT1section': {'CLIENT1key': None}} + >>> cfg + ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) + """ + out = {} + # scalars first + for i in range(len(self.scalars)): + entry = self.scalars[i] + try: + val = function(self, entry, **keywargs) + # bound again in case name has changed + entry = self.scalars[i] + out[entry] = val + except Exception: + if raise_errors: + raise + else: + entry = self.scalars[i] + out[entry] = False + # then sections + for i in range(len(self.sections)): + entry = self.sections[i] + if call_on_sections: + try: + function(self, entry, **keywargs) + except Exception: + if raise_errors: + raise + else: + entry = self.sections[i] + out[entry] = False + # bound again in case name has changed + entry = self.sections[i] + # previous result is discarded + out[entry] = self[entry].walk( + function, + raise_errors=raise_errors, + call_on_sections=call_on_sections, + **keywargs) + return out + + + def as_bool(self, key): + """ + Accepts a key as input. The corresponding value must be a string or + the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to + retain compatibility with Python 2.2. + + If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns + ``True``. + + If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns + ``False``. + + ``as_bool`` is not case sensitive. + + Any other input will raise a ``ValueError``. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_bool('a') + Traceback (most recent call last): + ValueError: Value "fish" is neither True nor False + >>> a['b'] = 'True' + >>> a.as_bool('b') + 1 + >>> a['b'] = 'off' + >>> a.as_bool('b') + 0 + """ + val = self[key] + if val == True: + return True + elif val == False: + return False + else: + try: + if not isinstance(val, six.string_types): + # TODO: Why do we raise a KeyError here? + raise KeyError() + else: + return self.main._bools[val.lower()] + except KeyError: + raise ValueError('Value "%s" is neither True nor False' % val) + + + def as_int(self, key): + """ + A convenience method which coerces the specified value to an integer. + + If the value is an invalid literal for ``int``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_int('a') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: 'fish' + >>> a['b'] = '1' + >>> a.as_int('b') + 1 + >>> a['b'] = '3.2' + >>> a.as_int('b') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: '3.2' + """ + return int(self[key]) + + + def as_float(self, key): + """ + A convenience method which coerces the specified value to a float. + + If the value is an invalid literal for ``float``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: invalid literal for float(): fish + >>> a['b'] = '1' + >>> a.as_float('b') + 1.0 + >>> a['b'] = '3.2' + >>> a.as_float('b') #doctest: +ELLIPSIS + 3.2... + """ + return float(self[key]) + + + def as_list(self, key): + """ + A convenience method which fetches the specified value, guaranteeing + that it is a list. + + >>> a = ConfigObj() + >>> a['a'] = 1 + >>> a.as_list('a') + [1] + >>> a['a'] = (1,) + >>> a.as_list('a') + [1] + >>> a['a'] = [1] + >>> a.as_list('a') + [1] + """ + result = self[key] + if isinstance(result, (tuple, list)): + return list(result) + return [result] + + + def restore_default(self, key): + """ + Restore (and return) default value for the specified key. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + If there is no default value for this key, ``KeyError`` is raised. + """ + default = self.default_values[key] + dict.__setitem__(self, key, default) + if key not in self.defaults: + self.defaults.append(key) + return default + + + def restore_defaults(self): + """ + Recursively restore default values to all members + that have them. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + It doesn't delete or modify entries without default values. + """ + for key in self.default_values: + self.restore_default(key) + + for section in self.sections: + self[section].restore_defaults() + + +class ConfigObj(Section): + """An object to read, create, and write config files.""" + + _keyword = re.compile(r'''^ # line start + (\s*) # indentation + ( # keyword + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'"=].*?) # no quotes + ) + \s*=\s* # divider + (.*) # value (including list values and comments) + $ # line end + ''', + re.VERBOSE) + + _sectionmarker = re.compile(r'''^ + (\s*) # 1: indentation + ((?:\[\s*)+) # 2: section marker open + ( # 3: section name open + (?:"\s*\S.*?\s*")| # at least one non-space with double quotes + (?:'\s*\S.*?\s*')| # at least one non-space with single quotes + (?:[^'"\s].*?) # at least one non-space unquoted + ) # section name close + ((?:\s*\])+) # 4: section marker close + \s*(\#.*)? # 5: optional comment + $''', + re.VERBOSE) + + # this regexp pulls list values out as a single string + # or single values and comments + # FIXME: this regex adds a '' to the end of comma terminated lists + # workaround in ``_handle_value`` + _valueexp = re.compile(r'''^ + (?: + (?: + ( + (?: + (?: + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#][^,\#]*?) # unquoted + ) + \s*,\s* # comma + )* # match all list items ending in a comma (if any) + ) + ( + (?:".*?")| # double quotes + (?:'.*?')| # single quotes + (?:[^'",\#\s][^,]*?)| # unquoted + (?:(? 1: + msg = "Parsing failed with several errors.\nFirst error %s" % info + error = ConfigObjError(msg) + else: + error = self._errors[0] + # set the errors attribute; it's a list of tuples: + # (error_type, message, line_number) + error.errors = self._errors + # set the config attribute + error.config = self + raise error + # delete private attributes + del self._errors + + if configspec is None: + self.configspec = None + else: + self._handle_configspec(configspec) + + + def _initialise(self, options=None): + if options is None: + options = OPTION_DEFAULTS + + # initialise a few variables + self.filename = None + self._errors = [] + self.raise_errors = options['raise_errors'] + self.interpolation = options['interpolation'] + self.list_values = options['list_values'] + self.create_empty = options['create_empty'] + self.file_error = options['file_error'] + self.stringify = options['stringify'] + self.indent_type = options['indent_type'] + self.encoding = options['encoding'] + self.default_encoding = options['default_encoding'] + self.BOM = False + self.newlines = None + self.write_empty_values = options['write_empty_values'] + self.unrepr = options['unrepr'] + + self.initial_comment = [] + self.final_comment = [] + self.configspec = None + + if self._inspec: + self.list_values = False + + # Clear section attributes as well + Section._initialise(self) + + + def __repr__(self): + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + return ('ConfigObj({%s})' % + ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) + for key in (self.scalars + self.sections)])) + + + def _handle_bom(self, infile): + """ + Handle any BOM, and decode if necessary. + + If an encoding is specified, that *must* be used - but the BOM should + still be removed (and the BOM attribute set). + + (If the encoding is wrongly specified, then a BOM for an alternative + encoding won't be discovered or removed.) + + If an encoding is not specified, UTF8 or UTF16 BOM will be detected and + removed. The BOM attribute will be set. UTF16 will be decoded to + unicode. + + NOTE: This method must not be called with an empty ``infile``. + + Specifying the *wrong* encoding is likely to cause a + ``UnicodeDecodeError``. + + ``infile`` must always be returned as a list of lines, but may be + passed in as a single string. + """ + + if ((self.encoding is not None) and + (self.encoding.lower() not in BOM_LIST)): + # No need to check for a BOM + # the encoding specified doesn't have one + # just decode + return self._decode(infile, self.encoding) + + if isinstance(infile, (list, tuple)): + line = infile[0] + else: + line = infile + + if isinstance(line, six.text_type): + # it's already decoded and there's no need to do anything + # else, just use the _decode utility method to handle + # listifying appropriately + return self._decode(infile, self.encoding) + + if self.encoding is not None: + # encoding explicitly supplied + # And it could have an associated BOM + # TODO: if encoding is just UTF16 - we ought to check for both + # TODO: big endian and little endian versions. + enc = BOM_LIST[self.encoding.lower()] + if enc == 'utf_16': + # For UTF16 we try big endian and little endian + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if not final_encoding: + # skip UTF8 + continue + if infile.startswith(BOM): + ### BOM discovered + ##self.BOM = True + # Don't need to remove BOM + return self._decode(infile, encoding) + + # If we get this far, will *probably* raise a DecodeError + # As it doesn't appear to start with a BOM + return self._decode(infile, self.encoding) + + # Must be UTF8 + BOM = BOM_SET[enc] + if not line.startswith(BOM): + return self._decode(infile, self.encoding) + + newline = line[len(BOM):] + + # BOM removed + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + self.BOM = True + return self._decode(infile, self.encoding) + + # No encoding specified - so we need to check for UTF8/UTF16 + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if not isinstance(line, six.binary_type) or not line.startswith(BOM): + # didn't specify a BOM, or it's not a bytestring + continue + else: + # BOM discovered + self.encoding = final_encoding + if not final_encoding: + self.BOM = True + # UTF8 + # remove BOM + newline = line[len(BOM):] + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + # UTF-8 + if isinstance(infile, six.text_type): + return infile.splitlines(True) + elif isinstance(infile, six.binary_type): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') + # UTF16 - have to decode + return self._decode(infile, encoding) + + + if six.PY2 and isinstance(line, str): + # don't actually do any decoding, since we're on python 2 and + # returning a bytestring is fine + return self._decode(infile, None) + # No BOM discovered and no encoding specified, default to UTF-8 + if isinstance(infile, six.binary_type): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') + + + def _a_to_u(self, aString): + """Decode ASCII strings to unicode if a self.encoding is specified.""" + if isinstance(aString, six.binary_type) and self.encoding: + return aString.decode(self.encoding) + else: + return aString + + + def _decode(self, infile, encoding): + """ + Decode infile to unicode. Using the specified encoding. + + if is a string, it also needs converting to a list. + """ + if isinstance(infile, six.string_types): + return infile.splitlines(True) + if isinstance(infile, six.binary_type): + # NOTE: Could raise a ``UnicodeDecodeError`` + if encoding: + return infile.decode(encoding).splitlines(True) + else: + return infile.splitlines(True) + + if encoding: + for i, line in enumerate(infile): + if isinstance(line, six.binary_type): + # NOTE: The isinstance test here handles mixed lists of unicode/string + # NOTE: But the decode will break on any non-string values + # NOTE: Or could raise a ``UnicodeDecodeError`` + infile[i] = line.decode(encoding) + return infile + + + def _decode_element(self, line): + """Decode element to unicode if necessary.""" + if isinstance(line, six.binary_type) and self.default_encoding: + return line.decode(self.default_encoding) + else: + return line + + + # TODO: this may need to be modified + def _str(self, value): + """ + Used by ``stringify`` within validate, to turn non-string values + into strings. + """ + if not isinstance(value, six.string_types): + # intentially 'str' because it's just whatever the "normal" + # string type is for the python version we're dealing with + return str(value) + else: + return value + + + def _parse(self, infile): + """Actually parse the config file.""" + temp_list_values = self.list_values + if self.unrepr: + self.list_values = False + + comment_list = [] + done_start = False + this_section = self + maxline = len(infile) - 1 + cur_index = -1 + reset_comment = False + + while cur_index < maxline: + if reset_comment: + comment_list = [] + cur_index += 1 + line = infile[cur_index] + sline = line.strip() + # do we have anything on the line ? + if not sline or sline.startswith('#'): + reset_comment = False + comment_list.append(line) + continue + + if not done_start: + # preserve initial comment + self.initial_comment = comment_list + comment_list = [] + done_start = True + + reset_comment = True + # first we check if it's a section marker + mat = self._sectionmarker.match(line) + if mat is not None: + # is a section line + (indent, sect_open, sect_name, sect_close, comment) = mat.groups() + if indent and (self.indent_type is None): + self.indent_type = indent + cur_depth = sect_open.count('[') + if cur_depth != sect_close.count(']'): + self._handle_error("Cannot compute the section depth", + NestingError, infile, cur_index) + continue + + if cur_depth < this_section.depth: + # the new section is dropping back to a previous level + try: + parent = self._match_depth(this_section, + cur_depth).parent + except SyntaxError: + self._handle_error("Cannot compute nesting level", + NestingError, infile, cur_index) + continue + elif cur_depth == this_section.depth: + # the new section is a sibling of the current section + parent = this_section.parent + elif cur_depth == this_section.depth + 1: + # the new section is a child the current section + parent = this_section + else: + self._handle_error("Section too nested", + NestingError, infile, cur_index) + continue + + sect_name = self._unquote(sect_name) + if sect_name in parent: + self._handle_error('Duplicate section name', + DuplicateError, infile, cur_index) + continue + + # create the new section + this_section = Section( + parent, + cur_depth, + self, + name=sect_name) + parent[sect_name] = this_section + parent.inline_comments[sect_name] = comment + parent.comments[sect_name] = comment_list + continue + # + # it's not a section marker, + # so it should be a valid ``key = value`` line + mat = self._keyword.match(line) + if mat is None: + self._handle_error( + 'Invalid line ({0!r}) (matched as neither section nor keyword)'.format(line), + ParseError, infile, cur_index) + else: + # is a keyword value + # value will include any inline comment + (indent, key, value) = mat.groups() + if indent and (self.indent_type is None): + self.indent_type = indent + # check for a multiline value + if value[:3] in ['"""', "'''"]: + try: + value, comment, cur_index = self._multiline( + value, infile, cur_index, maxline) + except SyntaxError: + self._handle_error( + 'Parse error in multiline value', + ParseError, infile, cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + if type(e) == UnknownType: + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing multiline value' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + if isinstance(e, UnknownType): + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing value' + self._handle_error(msg, UnreprError, infile, + cur_index) + continue + else: + # extract comment and lists + try: + (value, comment) = self._handle_value(value) + except SyntaxError: + self._handle_error( + 'Parse error in value', + ParseError, infile, cur_index) + continue + # + key = self._unquote(key) + if key in this_section: + self._handle_error( + 'Duplicate keyword name', + DuplicateError, infile, cur_index) + continue + # add the key. + # we set unrepr because if we have got this far we will never + # be creating a new section + this_section.__setitem__(key, value, unrepr=True) + this_section.inline_comments[key] = comment + this_section.comments[key] = comment_list + continue + # + if self.indent_type is None: + # no indentation used, set the type accordingly + self.indent_type = '' + + # preserve the final comment + if not self and not self.initial_comment: + self.initial_comment = comment_list + elif not reset_comment: + self.final_comment = comment_list + self.list_values = temp_list_values + + + def _match_depth(self, sect, depth): + """ + Given a section and a depth level, walk back through the sections + parents to see if the depth level matches a previous section. + + Return a reference to the right section, + or raise a SyntaxError. + """ + while depth < sect.depth: + if sect is sect.parent: + # we've reached the top level already + raise SyntaxError() + sect = sect.parent + if sect.depth == depth: + return sect + # shouldn't get here + raise SyntaxError() + + + def _handle_error(self, text, ErrorClass, infile, cur_index): + """ + Handle an error according to the error settings. + + Either raise the error or store it. + The error will have occured at ``cur_index`` + """ + line = infile[cur_index] + cur_index += 1 + message = '{0} at line {1}.'.format(text, cur_index) + error = ErrorClass(message, cur_index, line) + if self.raise_errors: + # raise the error - parsing stops here + raise error + # store the error + # reraise when parsing has finished + self._errors.append(error) + + + def _unquote(self, value): + """Return an unquoted version of a value""" + if not value: + # should only happen during parsing of lists + raise SyntaxError + if (value[0] == value[-1]) and (value[0] in ('"', "'")): + value = value[1:-1] + return value + + + def _quote(self, value, multiline=True): + """ + Return a safely quoted version of a value. + + Raise a ConfigObjError if the value cannot be safely quoted. + If multiline is ``True`` (default) then use triple quotes + if necessary. + + * Don't quote values that don't need it. + * Recursively quote members of a list and return a comma joined list. + * Multiline is ``False`` for lists. + * Obey list syntax for empty and single member lists. + + If ``list_values=False`` then the value is only quoted if it contains + a ``\\n`` (is multiline) or '#'. + + If ``write_empty_values`` is set, and the value is an empty string, it + won't be quoted. + """ + if multiline and self.write_empty_values and value == '': + # Only if multiline is set, so that it is used for values not + # keys, and not values that are part of a list + return '' + + if multiline and isinstance(value, (list, tuple)): + if not value: + return ',' + elif len(value) == 1: + return self._quote(value[0], multiline=False) + ',' + return ', '.join([self._quote(val, multiline=False) + for val in value]) + if not isinstance(value, six.string_types): + if self.stringify: + # intentially 'str' because it's just whatever the "normal" + # string type is for the python version we're dealing with + value = str(value) + else: + raise TypeError('Value "%s" is not a string.' % value) + + if not value: + return '""' + + no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value + need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value )) + hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value) + check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote + + if check_for_single: + if not self.list_values: + # we don't quote if ``list_values=False`` + quot = noquot + # for normal values either single or double quotes will do + elif '\n' in value: + # will only happen if multiline is off - e.g. '\n' in key + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + elif ((value[0] not in wspace_plus) and + (value[-1] not in wspace_plus) and + (',' not in value)): + quot = noquot + else: + quot = self._get_single_quote(value) + else: + # if value has '\n' or "'" *and* '"', it will need triple quotes + quot = self._get_triple_quote(value) + + if quot == noquot and '#' in value and self.list_values: + quot = self._get_single_quote(value) + + return quot % value + + + def _get_single_quote(self, value): + if ("'" in value) and ('"' in value): + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + elif '"' in value: + quot = squot + else: + quot = dquot + return quot + + + def _get_triple_quote(self, value): + if (value.find('"""') != -1) and (value.find("'''") != -1): + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + if value.find('"""') == -1: + quot = tdquot + else: + quot = tsquot + return quot + + + def _handle_value(self, value): + """ + Given a value string, unquote, remove comment, + handle lists. (including empty and single member lists) + """ + if self._inspec: + # Parsing a configspec so don't handle comments + return (value, '') + # do we look for lists in values ? + if not self.list_values: + mat = self._nolistvalue.match(value) + if mat is None: + raise SyntaxError() + # NOTE: we don't unquote here + return mat.groups() + # + mat = self._valueexp.match(value) + if mat is None: + # the value is badly constructed, probably badly quoted, + # or an invalid list + raise SyntaxError() + (list_values, single, empty_list, comment) = mat.groups() + if (list_values == '') and (single is None): + # change this if you want to accept empty values + raise SyntaxError() + # NOTE: note there is no error handling from here if the regex + # is wrong: then incorrect values will slip through + if empty_list is not None: + # the single comma - meaning an empty list + return ([], comment) + if single is not None: + # handle empty values + if list_values and not single: + # FIXME: the '' is a workaround because our regex now matches + # '' at the end of a list if it has a trailing comma + single = None + else: + single = single or '""' + single = self._unquote(single) + if list_values == '': + # not a list value + return (single, comment) + the_list = self._listvalueexp.findall(list_values) + the_list = [self._unquote(val) for val in the_list] + if single is not None: + the_list += [single] + return (the_list, comment) + + + def _multiline(self, value, infile, cur_index, maxline): + """Extract the value, where we are in a multiline situation.""" + quot = value[:3] + newvalue = value[3:] + single_line = self._triple_quote[quot][0] + multi_line = self._triple_quote[quot][1] + mat = single_line.match(value) + if mat is not None: + retval = list(mat.groups()) + retval.append(cur_index) + return retval + elif newvalue.find(quot) != -1: + # somehow the triple quote is missing + raise SyntaxError() + # + while cur_index < maxline: + cur_index += 1 + newvalue += '\n' + line = infile[cur_index] + if line.find(quot) == -1: + newvalue += line + else: + # end of multiline, process it + break + else: + # we've got to the end of the config, oops... + raise SyntaxError() + mat = multi_line.match(line) + if mat is None: + # a badly formed line + raise SyntaxError() + (value, comment) = mat.groups() + return (newvalue + value, comment, cur_index) + + + def _handle_configspec(self, configspec): + """Parse the configspec.""" + # FIXME: Should we check that the configspec was created with the + # correct settings ? (i.e. ``list_values=False``) + if not isinstance(configspec, ConfigObj): + try: + configspec = ConfigObj(configspec, + raise_errors=True, + file_error=True, + _inspec=True) + except ConfigObjError as e: + # FIXME: Should these errors have a reference + # to the already parsed ConfigObj ? + raise ConfigspecError('Parsing configspec failed: %s' % e) + except IOError as e: + raise IOError('Reading configspec failed: %s' % e) + + self.configspec = configspec + + + + def _set_configspec(self, section, copy): + """ + Called by validate. Handles setting the configspec on subsections + including sections to be validated by __many__ + """ + configspec = section.configspec + many = configspec.get('__many__') + if isinstance(many, dict): + for entry in section.sections: + if entry not in configspec: + section[entry].configspec = many + + for entry in configspec.sections: + if entry == '__many__': + continue + if entry not in section: + section[entry] = {} + section[entry]._created = True + if copy: + # copy comments + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + + # Could be a scalar when we expect a section + if isinstance(section[entry], Section): + section[entry].configspec = configspec[entry] + + + def _write_line(self, indent_string, entry, this_entry, comment): + """Write an individual line, for the write method""" + # NOTE: the calls to self._quote here handles non-StringType values. + if not self.unrepr: + val = self._decode_element(self._quote(this_entry)) + else: + val = repr(this_entry) + return '%s%s%s%s%s' % (indent_string, + self._decode_element(self._quote(entry, multiline=False)), + self._a_to_u(' = '), + val, + self._decode_element(comment)) + + + def _write_marker(self, indent_string, depth, entry, comment): + """Write a section marker line""" + return '%s%s%s%s%s' % (indent_string, + self._a_to_u('[' * depth), + self._quote(self._decode_element(entry), multiline=False), + self._a_to_u(']' * depth), + self._decode_element(comment)) + + + def _handle_comment(self, comment): + """Deal with a comment.""" + if not comment: + return '' + start = self.indent_type + if not comment.startswith('#'): + start += self._a_to_u(' # ') + return (start + comment) + + + # Public methods + + def write(self, outfile=None, section=None): + """ + Write the current ConfigObj as a file + + tekNico: FIXME: use StringIO instead of real files + + >>> filename = a.filename + >>> a.filename = 'test.ini' + >>> a.write() + >>> a.filename = filename + >>> a == ConfigObj('test.ini', raise_errors=True) + 1 + >>> import os + >>> os.remove('test.ini') + """ + if self.indent_type is None: + # this can be true if initialised from a dictionary + self.indent_type = DEFAULT_INDENT_TYPE + + out = [] + cs = self._a_to_u('#') + csp = self._a_to_u('# ') + if section is None: + int_val = self.interpolation + self.interpolation = False + section = self + for line in self.initial_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + + indent_string = self.indent_type * section.depth + for entry in (section.scalars + section.sections): + if entry in section.defaults: + # don't write out default values + continue + for comment_line in section.comments[entry]: + comment_line = self._decode_element(comment_line.lstrip()) + if comment_line and not comment_line.startswith(cs): + comment_line = csp + comment_line + out.append(indent_string + comment_line) + this_entry = section[entry] + comment = self._handle_comment(section.inline_comments[entry]) + + if isinstance(this_entry, Section): + # a section + out.append(self._write_marker( + indent_string, + this_entry.depth, + entry, + comment)) + out.extend(self.write(section=this_entry)) + else: + out.append(self._write_line( + indent_string, + entry, + this_entry, + comment)) + + if section is self: + for line in self.final_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line and not stripped_line.startswith(cs): + line = csp + line + out.append(line) + self.interpolation = int_val + + if section is not self: + return out + + if (self.filename is None) and (outfile is None): + # output a list of lines + # might need to encode + # NOTE: This will *screw* UTF16, each line will start with the BOM + if self.encoding: + out = [l.encode(self.encoding) for l in out] + if (self.BOM and ((self.encoding is None) or + (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): + # Add the UTF8 BOM + if not out: + out.append('') + out[0] = BOM_UTF8 + out[0] + return out + + # Turn the list to a string, joined with correct newlines + newline = self.newlines or os.linesep + if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w' + and sys.platform == 'win32' and newline == '\r\n'): + # Windows specific hack to avoid writing '\r\r\n' + newline = '\n' + output = self._a_to_u(newline).join(out) + if not output.endswith(newline): + output += newline + + if isinstance(output, six.binary_type): + output_bytes = output + else: + output_bytes = output.encode(self.encoding or + self.default_encoding or + 'ascii') + + if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)): + # Add the UTF8 BOM + output_bytes = BOM_UTF8 + output_bytes + + if outfile is not None: + outfile.write(output_bytes) + else: + with open(self.filename, 'wb') as h: + h.write(output_bytes) + + def validate(self, validator, preserve_errors=False, copy=False, + section=None): + """ + Test the ConfigObj against a configspec. + + It uses the ``validator`` object from *validate.py*. + + To run ``validate`` on the current ConfigObj, call: :: + + test = config.validate(validator) + + (Normally having previously passed in the configspec when the ConfigObj + was created - you can dynamically assign a dictionary of checks to the + ``configspec`` attribute of a section though). + + It returns ``True`` if everything passes, or a dictionary of + pass/fails (True/False). If every member of a subsection passes, it + will just have the value ``True``. (It also returns ``False`` if all + members fail). + + In addition, it converts the values from strings to their native + types if their checks pass (and ``stringify`` is set). + + If ``preserve_errors`` is ``True`` (``False`` is default) then instead + of a marking a fail with a ``False``, it will preserve the actual + exception object. This can contain info about the reason for failure. + For example the ``VdtValueTooSmallError`` indicates that the value + supplied was too small. If a value (or section) is missing it will + still be marked as ``False``. + + You must have the validate module to use ``preserve_errors=True``. + + You can then use the ``flatten_errors`` function to turn your nested + results dictionary into a flattened list of failures - useful for + displaying meaningful error messages. + """ + if section is None: + if self.configspec is None: + raise ValueError('No configspec supplied.') + if preserve_errors: + # We do this once to remove a top level dependency on the validate module + # Which makes importing configobj faster + from validate import VdtMissingValue + self._vdtMissingValue = VdtMissingValue + + section = self + + if copy: + section.initial_comment = section.configspec.initial_comment + section.final_comment = section.configspec.final_comment + section.encoding = section.configspec.encoding + section.BOM = section.configspec.BOM + section.newlines = section.configspec.newlines + section.indent_type = section.configspec.indent_type + + # + # section.default_values.clear() #?? + configspec = section.configspec + self._set_configspec(section, copy) + + + def validate_entry(entry, spec, val, missing, ret_true, ret_false): + section.default_values.pop(entry, None) + + try: + section.default_values[entry] = validator.get_default_value(configspec[entry]) + except (KeyError, AttributeError, validator.baseErrorClass): + # No default, bad default or validator has no 'get_default_value' + # (e.g. SimpleVal) + pass + + try: + check = validator.check(spec, + val, + missing=missing + ) + except validator.baseErrorClass as e: + if not preserve_errors or isinstance(e, self._vdtMissingValue): + out[entry] = False + else: + # preserve the error + out[entry] = e + ret_false = False + ret_true = False + else: + ret_false = False + out[entry] = True + if self.stringify or missing: + # if we are doing type conversion + # or the value is a supplied default + if not self.stringify: + if isinstance(check, (list, tuple)): + # preserve lists + check = [self._str(item) for item in check] + elif missing and check is None: + # convert the None from a default to a '' + check = '' + else: + check = self._str(check) + if (check != val) or missing: + section[entry] = check + if not copy and missing and entry not in section.defaults: + section.defaults.append(entry) + return ret_true, ret_false + + # + out = {} + ret_true = True + ret_false = True + + unvalidated = [k for k in section.scalars if k not in configspec] + incorrect_sections = [k for k in configspec.sections if k in section.scalars] + incorrect_scalars = [k for k in configspec.scalars if k in section.sections] + + for entry in configspec.scalars: + if entry in ('__many__', '___many___'): + # reserved names + continue + if (not entry in section.scalars) or (entry in section.defaults): + # missing entries + # or entries from defaults + missing = True + val = None + if copy and entry not in section.scalars: + # copy comments + section.comments[entry] = ( + configspec.comments.get(entry, [])) + section.inline_comments[entry] = ( + configspec.inline_comments.get(entry, '')) + # + else: + missing = False + val = section[entry] + + ret_true, ret_false = validate_entry(entry, configspec[entry], val, + missing, ret_true, ret_false) + + many = None + if '__many__' in configspec.scalars: + many = configspec['__many__'] + elif '___many___' in configspec.scalars: + many = configspec['___many___'] + + if many is not None: + for entry in unvalidated: + val = section[entry] + ret_true, ret_false = validate_entry(entry, many, val, False, + ret_true, ret_false) + unvalidated = [] + + for entry in incorrect_scalars: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Value %r was provided as a section' % entry + out[entry] = validator.baseErrorClass(msg) + for entry in incorrect_sections: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Section %r was provided as a single value' % entry + out[entry] = validator.baseErrorClass(msg) + + # Missing sections will have been created as empty ones when the + # configspec was read. + for entry in section.sections: + # FIXME: this means DEFAULT is not copied in copy mode + if section is self and entry == 'DEFAULT': + continue + if section[entry].configspec is None: + unvalidated.append(entry) + continue + if copy: + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry]) + out[entry] = check + if check == False: + ret_true = False + elif check == True: + ret_false = False + else: + ret_true = False + + section.extra_values = unvalidated + if preserve_errors and not section._created: + # If the section wasn't created (i.e. it wasn't missing) + # then we can't return False, we need to preserve errors + ret_false = False + # + if ret_false and preserve_errors and out: + # If we are preserving errors, but all + # the failures are from missing sections / values + # then we can return False. Otherwise there is a + # real failure that we need to preserve. + ret_false = not any(out.values()) + if ret_true: + return True + elif ret_false: + return False + return out + + + def reset(self): + """Clear ConfigObj instance and restore to 'freshly created' state.""" + self.clear() + self._initialise() + # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload) + # requires an empty dictionary + self.configspec = None + # Just to be sure ;-) + self._original_configspec = None + + + def reload(self): + """ + Reload a ConfigObj from file. + + This method raises a ``ReloadError`` if the ConfigObj doesn't have + a filename attribute pointing to a file. + """ + if not isinstance(self.filename, six.string_types): + raise ReloadError() + + filename = self.filename + current_options = {} + for entry in OPTION_DEFAULTS: + if entry == 'configspec': + continue + current_options[entry] = getattr(self, entry) + + configspec = self._original_configspec + current_options['configspec'] = configspec + + self.clear() + self._initialise(current_options) + self._load(filename, configspec) + + + +class SimpleVal(object): + """ + A simple validator. + Can be used to check that all members expected are present. + + To use it, provide a configspec with all your members in (the value given + will be ignored). Pass an instance of ``SimpleVal`` to the ``validate`` + method of your ``ConfigObj``. ``validate`` will return ``True`` if all + members are present, or a dictionary with True/False meaning + present/missing. (Whole missing sections will be replaced with ``False``) + """ + + def __init__(self): + self.baseErrorClass = ConfigObjError + + def check(self, check, member, missing=False): + """A dummy check method, always returns the value unchanged.""" + if missing: + raise self.baseErrorClass() + return member + + +def flatten_errors(cfg, res, levels=None, results=None): + """ + An example function that will turn a nested dictionary of results + (as returned by ``ConfigObj.validate``) into a flat list. + + ``cfg`` is the ConfigObj instance being checked, ``res`` is the results + dictionary returned by ``validate``. + + (This is a recursive function, so you shouldn't use the ``levels`` or + ``results`` arguments - they are used by the function.) + + Returns a list of keys that failed. Each member of the list is a tuple:: + + ([list of sections...], key, result) + + If ``validate`` was called with ``preserve_errors=False`` (the default) + then ``result`` will always be ``False``. + + *list of sections* is a flattened list of sections that the key was found + in. + + If the section was missing (or a section was expected and a scalar provided + - or vice-versa) then key will be ``None``. + + If the value (or section) was missing then ``result`` will be ``False``. + + If ``validate`` was called with ``preserve_errors=True`` and a value + was present, but failed the check, then ``result`` will be the exception + object returned. You can use this as a string that describes the failure. + + For example *The value "3" is of the wrong type*. + """ + if levels is None: + # first time called + levels = [] + results = [] + if res == True: + return sorted(results) + if res == False or isinstance(res, Exception): + results.append((levels[:], None, res)) + if levels: + levels.pop() + return sorted(results) + for (key, val) in list(res.items()): + if val == True: + continue + if isinstance(cfg.get(key), dict): + # Go down one level + levels.append(key) + flatten_errors(cfg[key], val, levels, results) + continue + results.append((levels[:], key, val)) + # + # Go up one level + if levels: + levels.pop() + # + return sorted(results) + + +def get_extra_values(conf, _prepend=()): + """ + Find all the values and sections not in the configspec from a validated + ConfigObj. + + ``get_extra_values`` returns a list of tuples where each tuple represents + either an extra section, or an extra value. + + The tuples contain two values, a tuple representing the section the value + is in and the name of the extra values. For extra values in the top level + section the first member will be an empty tuple. For values in the 'foo' + section the first member will be ``('foo',)``. For members in the 'bar' + subsection of the 'foo' section the first member will be ``('foo', 'bar')``. + + NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't + been validated it will return an empty list. + """ + out = [] + + out.extend([(_prepend, name) for name in conf.extra_values]) + for name in conf.sections: + if name not in conf.extra_values: + out.extend(get_extra_values(conf[name], _prepend + (name,))) + return out + + +"""*A programming language is a medium of expression.* - Paul Graham""" diff --git a/libs/common/six.py b/libs/common/six.py index 89b2188f..4e15675d 100644 --- a/libs/common/six.py +++ b/libs/common/six.py @@ -1,4 +1,4 @@ -# Copyright (c) 2010-2018 Benjamin Peterson +# Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.12.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -71,6 +71,11 @@ else: MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -186,6 +191,11 @@ class _SixMetaPathImporter(object): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -223,6 +233,12 @@ class _SixMetaPathImporter(object): return None get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + _importer = _SixMetaPathImporter(__name__) @@ -255,9 +271,11 @@ _moved_attributes = [ MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), @@ -637,13 +655,16 @@ if PY3: import io StringIO = io.StringIO BytesIO = io.BytesIO + del io _assertCountEqual = "assertCountEqual" if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" else: def b(s): return s @@ -665,6 +686,7 @@ else: _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") @@ -681,6 +703,10 @@ def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") @@ -716,16 +742,7 @@ else: """) -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""") -elif sys.version_info[:2] > (3, 2): +if sys.version_info[:2] > (3,): exec_("""def raise_from(value, from_value): try: raise value from from_value @@ -805,13 +822,33 @@ if sys.version_info[:2] < (3, 3): _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + else: wraps = functools.wraps @@ -824,7 +861,15 @@ def with_metaclass(meta, *bases): class metaclass(type): def __new__(cls, name, this_bases, d): - return meta(name, bases, d) + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) @classmethod def __prepare__(cls, name, this_bases): @@ -861,12 +906,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding='utf-8', errors='strict'): @@ -880,12 +924,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s @@ -908,10 +955,9 @@ def ensure_text(s, encoding='utf-8', errors='strict'): raise TypeError("not expecting type '%s'" % type(s)) - def python_2_unicode_compatible(klass): """ - A decorator that defines __unicode__ and __str__ methods under Python 2. + A class decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method From 5073ec0c6f2c56f9f1f5032a0912571cd4a4273f Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 06:15:51 -0500 Subject: [PATCH 29/47] Update vendored pyxdg to 0.28 --- libs/common/xdg/BaseDirectory.py | 14 ++++++++ libs/common/xdg/IniFile.py | 59 ++++++++++++++++---------------- libs/common/xdg/Menu.py | 36 +++++++++++++------ libs/common/xdg/MenuEditor.py | 2 +- libs/common/xdg/Mime.py | 9 +++-- libs/common/xdg/RecentFiles.py | 39 ++++++++++----------- libs/common/xdg/__init__.py | 2 +- 7 files changed, 95 insertions(+), 66 deletions(-) diff --git a/libs/common/xdg/BaseDirectory.py b/libs/common/xdg/BaseDirectory.py index a7c31b1b..3c3afe89 100644 --- a/libs/common/xdg/BaseDirectory.py +++ b/libs/common/xdg/BaseDirectory.py @@ -43,6 +43,9 @@ xdg_config_dirs = [xdg_config_home] + \ xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \ os.path.join(_home, '.cache') +xdg_state_home = os.environ.get('XDG_STATE_HOME') or \ + os.path.join(_home, '.local', 'state') + xdg_data_dirs = [x for x in xdg_data_dirs if x] xdg_config_dirs = [x for x in xdg_config_dirs if x] @@ -81,6 +84,17 @@ def save_cache_path(*resource): os.makedirs(path) return path +def save_state_path(*resource): + """Ensure ``$XDG_STATE_HOME//`` exists, and return its path. + 'resource' should normally be the name of your application or a shared + resource.""" + resource = os.path.join(*resource) + assert not resource.startswith('/') + path = os.path.join(xdg_state_home, resource) + if not os.path.isdir(path): + os.makedirs(path) + return path + def load_config_paths(*resource): """Returns an iterator which gives each directory named 'resource' in the configuration search path. Information provided by earlier directories should diff --git a/libs/common/xdg/IniFile.py b/libs/common/xdg/IniFile.py index 718589f9..84be6142 100644 --- a/libs/common/xdg/IniFile.py +++ b/libs/common/xdg/IniFile.py @@ -56,38 +56,37 @@ class IniFile: return # parse file - for line in fd: - line = line.strip() - # empty line - if not line: - continue - # comment - elif line[0] == '#': - continue - # new group - elif line[0] == '[': - currentGroup = line.lstrip("[").rstrip("]") - if debug and self.hasGroup(currentGroup): - raise DuplicateGroupError(currentGroup, filename) - else: - content[currentGroup] = {} - # key - else: - try: - key, value = line.split("=", 1) - except ValueError: - raise ParsingError("Invalid line: " + line, filename) - - key = key.strip() # Spaces before/after '=' should be ignored - try: - if debug and self.hasKey(key, currentGroup): - raise DuplicateKeyError(key, currentGroup, filename) + with fd: + for line in fd: + line = line.strip() + # empty line + if not line: + continue + # comment + elif line[0] == '#': + continue + # new group + elif line[0] == '[': + currentGroup = line.lstrip("[").rstrip("]") + if debug and self.hasGroup(currentGroup): + raise DuplicateGroupError(currentGroup, filename) else: - content[currentGroup][key] = value.strip() - except (IndexError, UnboundLocalError): - raise ParsingError("Parsing error on key, group missing", filename) + content[currentGroup] = {} + # key + else: + try: + key, value = line.split("=", 1) + except ValueError: + raise ParsingError("Invalid line: " + line, filename) - fd.close() + key = key.strip() # Spaces before/after '=' should be ignored + try: + if debug and self.hasKey(key, currentGroup): + raise DuplicateKeyError(key, currentGroup, filename) + else: + content[currentGroup][key] = value.strip() + except (IndexError, UnboundLocalError): + raise ParsingError("Parsing error on key, group missing", filename) self.filename = filename self.tainted = False diff --git a/libs/common/xdg/Menu.py b/libs/common/xdg/Menu.py index 1d03cad5..1dd2af59 100644 --- a/libs/common/xdg/Menu.py +++ b/libs/common/xdg/Menu.py @@ -21,6 +21,7 @@ import os import locale import subprocess import ast +import sys try: import xml.etree.cElementTree as etree except ImportError: @@ -35,6 +36,17 @@ import xdg.Locale import xdg.Config +def _ast_const(name): + if sys.version_info >= (3, 4): + name = ast.literal_eval(name) + if sys.version_info >= (3, 8): + return ast.Constant(name) + else: + return ast.NameConstant(name) + else: + return ast.Name(id=name, ctx=ast.Load()) + + def _strxfrm(s): """Wrapper around locale.strxfrm that accepts unicode strings on Python 2. @@ -298,11 +310,11 @@ class Menu: entry.Show = NO_EXEC self.Visible -= 1 elif xdg.Config.windowmanager: - if (entry.DesktopEntry.OnlyShowIn != [] and ( - xdg.Config.windowmanager not in entry.DesktopEntry.OnlyShowIn + if (entry.DesktopEntry.getOnlyShowIn() != [] and ( + xdg.Config.windowmanager not in entry.DesktopEntry.getOnlyShowIn() ) ) or ( - xdg.Config.windowmanager in entry.DesktopEntry.NotShowIn + xdg.Config.windowmanager in entry.DesktopEntry.getNotShowIn() ): entry.Show = NOT_SHOW_IN self.Visible -= 1 @@ -710,11 +722,12 @@ class XMLMenuBuilder(object): inline_header=_to_bool(node.attrib.get("inline_header", True)), inline_alias=_to_bool(node.attrib.get("inline_alias", False)) ) + order = [] for child in node: tag, text = child.tag, child.text text = text.strip() if text else None if tag == "Menuname" and text: - layout.order.append([ + order.append([ "Menuname", text, _to_bool(child.attrib.get("show_empty", False)), @@ -724,14 +737,15 @@ class XMLMenuBuilder(object): _to_bool(child.attrib.get("inline_alias", False)) ]) elif tag == "Separator": - layout.order.append(['Separator']) + order.append(['Separator']) elif tag == "Filename" and text: - layout.order.append(["Filename", text]) + order.append(["Filename", text]) elif tag == "Merge": - layout.order.append([ + order.append([ "Merge", child.attrib.get("type", "all") ]) + layout.order = order return layout def parse_move(self, node): @@ -754,7 +768,7 @@ class XMLMenuBuilder(object): if expr: tree.body = expr else: - tree.body = ast.Name('False', ast.Load()) + tree.body = _ast_const('False') ast.fix_missing_locations(tree) return Rule(type, tree) @@ -781,7 +795,7 @@ class XMLMenuBuilder(object): expr = self.parse_bool_op(node, ast.Or()) return ast.UnaryOp(ast.Not(), expr) if expr else None elif tag == 'All': - return ast.Name('True', ast.Load()) + return _ast_const('True') elif tag == 'Category': category = node.text return ast.Compare( @@ -994,8 +1008,8 @@ class XMLMenuBuilder(object): menuentry = MenuEntry(directory, dir) if not menu.Directory: menu.Directory = menuentry - elif menuentry.Type == MenuEntry.TYPE_SYSTEM: - if menu.Directory.Type == MenuEntry.TYPE_USER: + elif menuentry.getType() == MenuEntry.TYPE_SYSTEM: + if menu.Directory.getType() == MenuEntry.TYPE_USER: menu.Directory.Original = menuentry if menu.Directory: break diff --git a/libs/common/xdg/MenuEditor.py b/libs/common/xdg/MenuEditor.py index 25b8e834..ee880f47 100644 --- a/libs/common/xdg/MenuEditor.py +++ b/libs/common/xdg/MenuEditor.py @@ -54,7 +54,7 @@ class MenuEditor(object): try: self.tree = etree.parse(self.filename) except IOError: - root = etree.fromtring(""" + root = etree.fromstring("""

    Applications diff --git a/libs/common/xdg/Mime.py b/libs/common/xdg/Mime.py index 3bff8b26..886cb42a 100644 --- a/libs/common/xdg/Mime.py +++ b/libs/common/xdg/Mime.py @@ -749,14 +749,16 @@ def install_mime_info(application, package_file): file with the same name (if the contents are different)""" application += '.xml' - new_data = open(package_file).read() + with open(package_file) as f: + new_data = f.read() # See if the file is already installed package_dir = os.path.join('mime', 'packages') resource = os.path.join(package_dir, application) for x in BaseDirectory.load_data_paths(resource): try: - old_data = open(x).read() + with open(x) as f: + old_data = f.read() except: continue if old_data == new_data: @@ -770,7 +772,8 @@ def install_mime_info(application, package_file): new_file = os.path.join(BaseDirectory.save_data_path(package_dir), application) # Write the file... - open(new_file, 'w').write(new_data) + with open(new_file, 'w') as f: + f.write(new_data) # Update the database... command = 'update-mime-database' diff --git a/libs/common/xdg/RecentFiles.py b/libs/common/xdg/RecentFiles.py index 3038b578..7ee7ee57 100644 --- a/libs/common/xdg/RecentFiles.py +++ b/libs/common/xdg/RecentFiles.py @@ -71,28 +71,27 @@ class RecentFiles: elif not filename: filename = self.filename - f = open(filename, "w") - fcntl.lockf(f, fcntl.LOCK_EX) - f.write('\n') - f.write("\n") + with open(filename, "w") as f: + fcntl.lockf(f, fcntl.LOCK_EX) + f.write('\n') + f.write("\n") - for r in self.RecentFiles: - f.write(" \n") - f.write(" %s\n" % xml.sax.saxutils.escape(r.URI)) - f.write(" %s\n" % r.MimeType) - f.write(" %s\n" % r.Timestamp) - if r.Private == True: - f.write(" \n") - if len(r.Groups) > 0: - f.write(" \n") - for group in r.Groups: - f.write(" %s\n" % group) - f.write(" \n") - f.write(" \n") + for r in self.RecentFiles: + f.write(" \n") + f.write(" %s\n" % xml.sax.saxutils.escape(r.URI)) + f.write(" %s\n" % r.MimeType) + f.write(" %s\n" % r.Timestamp) + if r.Private == True: + f.write(" \n") + if len(r.Groups) > 0: + f.write(" \n") + for group in r.Groups: + f.write(" %s\n" % group) + f.write(" \n") + f.write(" \n") - f.write("\n") - fcntl.lockf(f, fcntl.LOCK_UN) - f.close() + f.write("\n") + fcntl.lockf(f, fcntl.LOCK_UN) def getFiles(self, mimetypes=None, groups=None, limit=0): """Get a list of recently used files. diff --git a/libs/common/xdg/__init__.py b/libs/common/xdg/__init__.py index b5a117ea..a417f61c 100644 --- a/libs/common/xdg/__init__.py +++ b/libs/common/xdg/__init__.py @@ -1,3 +1,3 @@ __all__ = [ "BaseDirectory", "DesktopEntry", "Menu", "Exceptions", "IniFile", "IconTheme", "Locale", "Config", "Mime", "RecentFiles", "MenuEditor" ] -__version__ = "0.26" +__version__ = "0.28" From 56c6773c6b5f29fefe21bd934b2d06109888838a Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 18:02:40 -0500 Subject: [PATCH 30/47] Update vendored beets to 1.6.0 Updates colorama to 0.4.6 Adds confuse version 1.7.0 Updates jellyfish to 0.9.0 Adds mediafile 0.10.1 Updates munkres to 1.1.4 Updates musicbrainzngs to 0.7.1 Updates mutagen to 1.46.0 Updates pyyaml to 6.0 Updates unidecode to 1.3.6 --- libs/common/_yaml/__init__.py | 33 + libs/common/beets/__init__.py | 26 +- libs/common/beets/__main__.py | 2 - libs/common/beets/art.py | 72 +- libs/common/beets/autotag/__init__.py | 111 +- libs/common/beets/autotag/hooks.py | 209 +- libs/common/beets/autotag/match.py | 55 +- libs/common/beets/autotag/mb.py | 186 +- libs/common/beets/config_default.yaml | 15 + libs/common/beets/dbcore/__init__.py | 2 - libs/common/beets/dbcore/db.py | 372 ++- libs/common/beets/dbcore/query.py | 108 +- libs/common/beets/dbcore/queryparse.py | 70 +- libs/common/beets/dbcore/types.py | 70 +- libs/common/beets/importer.py | 204 +- libs/common/beets/library.py | 574 ++-- libs/common/beets/logging.py | 16 +- libs/common/beets/mediafile.py | 2098 +------------- libs/common/beets/plugins.py | 271 +- libs/common/beets/random.py | 113 + libs/common/beets/ui/__init__.py | 351 +-- libs/common/beets/ui/commands.py | 727 ++--- libs/common/beets/util/__init__.py | 292 +- libs/common/beets/util/artresizer.py | 361 ++- libs/common/beets/util/bluelet.py | 24 +- libs/common/beets/util/confit.py | 1507 +---------- libs/common/beets/util/enumeration.py | 2 - libs/common/beets/util/functemplate.py | 169 +- libs/common/beets/util/hidden.py | 2 - libs/common/beets/util/pipeline.py | 50 +- libs/common/beets/vfs.py | 2 - libs/common/beetsplug/__init__.py | 2 - libs/common/beetsplug/absubmit.py | 77 +- libs/common/beetsplug/acousticbrainz.py | 87 +- libs/common/beetsplug/albumtypes.py | 65 + libs/common/beetsplug/aura.py | 984 +++++++ libs/common/beetsplug/badfiles.py | 181 +- libs/common/beetsplug/bareasc.py | 82 + libs/common/beetsplug/beatport.py | 171 +- libs/common/beetsplug/bench.py | 2 - libs/common/beetsplug/bpd/__init__.py | 833 ++++-- libs/common/beetsplug/bpd/gstplayer.py | 73 +- libs/common/beetsplug/bpm.py | 23 +- libs/common/beetsplug/bpsync.py | 186 ++ libs/common/beetsplug/bucket.py | 27 +- libs/common/beetsplug/chroma.py | 105 +- libs/common/beetsplug/convert.py | 254 +- libs/common/beetsplug/cue.py | 57 - libs/common/beetsplug/deezer.py | 230 ++ libs/common/beetsplug/discogs.py | 254 +- libs/common/beetsplug/duplicates.py | 100 +- libs/common/beetsplug/edit.py | 65 +- libs/common/beetsplug/embedart.py | 60 +- libs/common/beetsplug/embyupdate.py | 29 +- libs/common/beetsplug/export.py | 194 +- libs/common/beetsplug/fetchart.py | 664 +++-- libs/common/beetsplug/filefilter.py | 8 +- libs/common/beetsplug/fish.py | 285 ++ libs/common/beetsplug/freedesktop.py | 12 +- libs/common/beetsplug/fromfilename.py | 7 +- libs/common/beetsplug/ftintitle.py | 20 +- libs/common/beetsplug/fuzzy.py | 4 +- libs/common/beetsplug/gmusic.py | 81 +- libs/common/beetsplug/hook.py | 57 +- libs/common/beetsplug/ihate.py | 18 +- libs/common/beetsplug/importadded.py | 39 +- libs/common/beetsplug/importfeeds.py | 10 +- libs/common/beetsplug/info.py | 117 +- libs/common/beetsplug/inline.py | 29 +- libs/common/beetsplug/ipfs.py | 49 +- libs/common/beetsplug/keyfinder.py | 42 +- libs/common/beetsplug/kodiupdate.py | 25 +- libs/common/beetsplug/lastgenre/__init__.py | 120 +- .../beetsplug/lastgenre/genres-tree.yaml | 25 +- libs/common/beetsplug/lastgenre/genres.txt | 8 + libs/common/beetsplug/lastimport.py | 54 +- libs/common/beetsplug/loadext.py | 44 + libs/common/beetsplug/lyrics.py | 566 ++-- libs/common/beetsplug/mbcollection.py | 28 +- libs/common/beetsplug/mbsubmit.py | 11 +- libs/common/beetsplug/mbsync.py | 65 +- libs/common/beetsplug/metasync/__init__.py | 19 +- libs/common/beetsplug/metasync/amarok.py | 8 +- libs/common/beetsplug/metasync/itunes.py | 38 +- libs/common/beetsplug/missing.py | 30 +- libs/common/beetsplug/mpdstats.py | 127 +- libs/common/beetsplug/mpdupdate.py | 27 +- libs/common/beetsplug/parentwork.py | 211 ++ libs/common/beetsplug/permissions.py | 64 +- libs/common/beetsplug/play.py | 37 +- libs/common/beetsplug/playlist.py | 185 ++ libs/common/beetsplug/plexupdate.py | 64 +- libs/common/beetsplug/random.py | 108 +- libs/common/beetsplug/replaygain.py | 1040 ++++--- libs/common/beetsplug/rewrite.py | 10 +- libs/common/beetsplug/scrub.py | 30 +- libs/common/beetsplug/smartplaylist.py | 67 +- libs/common/beetsplug/sonosupdate.py | 10 +- libs/common/beetsplug/spotify.py | 540 +++- libs/common/beetsplug/subsonicplaylist.py | 171 ++ libs/common/beetsplug/subsonicupdate.py | 144 + libs/common/beetsplug/the.py | 26 +- libs/common/beetsplug/thumbnails.py | 69 +- libs/common/beetsplug/types.py | 6 +- libs/common/beetsplug/unimported.py | 68 + libs/common/beetsplug/web/__init__.py | 167 +- libs/common/beetsplug/web/static/beets.js | 2 +- libs/common/beetsplug/zero.py | 29 +- libs/common/bin/beet.exe | Bin 93012 -> 108369 bytes libs/common/bin/chardetect.exe | Bin 93026 -> 0 bytes libs/common/bin/easy_install-3.7.exe | Bin 93035 -> 0 bytes libs/common/bin/easy_install.exe | Bin 93035 -> 0 bytes libs/common/bin/guessit.exe | Bin 93020 -> 0 bytes libs/common/bin/mid3cp | 16 - libs/common/bin/mid3cp.exe | Bin 0 -> 108396 bytes libs/common/bin/mid3iconv | 16 - libs/common/bin/mid3iconv.exe | Bin 0 -> 108399 bytes libs/common/bin/mid3v2 | 16 - libs/common/bin/mid3v2.exe | Bin 0 -> 108396 bytes libs/common/bin/moggsplit | 16 - libs/common/bin/moggsplit.exe | Bin 0 -> 108399 bytes libs/common/bin/mutagen-inspect | 16 - libs/common/bin/mutagen-inspect.exe | Bin 0 -> 108405 bytes libs/common/bin/mutagen-pony | 16 - libs/common/bin/mutagen-pony.exe | Bin 0 -> 108402 bytes libs/common/bin/pbr.exe | Bin 93016 -> 0 bytes libs/common/bin/srt.exe | Bin 93018 -> 0 bytes libs/common/bin/subliminal.exe | Bin 93030 -> 0 bytes libs/common/bin/unidecode.exe | Bin 93018 -> 108375 bytes libs/common/colorama/__init__.py | 5 +- libs/common/colorama/ansi.py | 2 +- libs/common/colorama/ansitowin32.py | 46 +- libs/common/colorama/initialise.py | 51 +- libs/common/colorama/tests/__init__.py | 1 + libs/common/colorama/tests/ansi_test.py | 76 + .../common/colorama/tests/ansitowin32_test.py | 294 ++ libs/common/colorama/tests/initialise_test.py | 189 ++ libs/common/colorama/tests/isatty_test.py | 57 + libs/common/colorama/tests/utils.py | 49 + libs/common/colorama/tests/winterm_test.py | 131 + libs/common/colorama/win32.py | 28 + libs/common/colorama/winterm.py | 28 +- libs/common/confuse/__init__.py | 13 + libs/common/confuse/core.py | 724 +++++ libs/common/confuse/exceptions.py | 56 + libs/common/confuse/sources.py | 184 ++ libs/common/confuse/templates.py | 741 +++++ libs/common/confuse/util.py | 186 ++ libs/common/confuse/yaml_util.py | 228 ++ libs/common/jellyfish/__init__.py | 26 +- libs/common/jellyfish/__init__.pyi | 11 + libs/common/jellyfish/_jellyfish.py | 437 +-- .../jellyfish/cjellyfish.cp37-win_amd64.pyd | Bin 0 -> 31232 bytes libs/common/jellyfish/compat.py | 11 - libs/common/jellyfish/porter.py | 183 +- libs/common/jellyfish/py.typed | 0 libs/common/jellyfish/test.py | 176 +- libs/common/mediafile.py | 2398 +++++++++++++++++ libs/common/munkres.py | 469 +--- libs/common/musicbrainzngs/caa.py | 30 +- libs/common/musicbrainzngs/compat.py | 7 +- libs/common/musicbrainzngs/mbxml.py | 42 +- libs/common/musicbrainzngs/musicbrainz.py | 145 +- libs/common/mutagen/__init__.py | 3 +- libs/common/mutagen/_compat.py | 92 - libs/common/mutagen/_constants.py | 1 - libs/common/mutagen/_file.py | 20 +- libs/common/mutagen/_iff.py | 386 +++ libs/common/mutagen/_riff.py | 69 + libs/common/mutagen/_senf/__init__.py | 91 - libs/common/mutagen/_senf/_argv.py | 117 - libs/common/mutagen/_senf/_compat.py | 58 - libs/common/mutagen/_senf/_environ.py | 267 -- libs/common/mutagen/_senf/_fsnative.py | 666 ----- libs/common/mutagen/_senf/_print.py | 424 --- libs/common/mutagen/_senf/_stdlib.py | 154 -- libs/common/mutagen/_senf/_temp.py | 96 - libs/common/mutagen/_senf/_winansi.py | 319 --- libs/common/mutagen/_senf/_winapi.py | 222 -- libs/common/mutagen/_tags.py | 5 +- libs/common/mutagen/_tools/__init__.py | 1 - libs/common/mutagen/_tools/_util.py | 12 +- libs/common/mutagen/_tools/mid3cp.py | 30 +- libs/common/mutagen/_tools/mid3iconv.py | 17 +- libs/common/mutagen/_tools/mid3v2.py | 100 +- libs/common/mutagen/_tools/moggsplit.py | 5 +- libs/common/mutagen/_tools/mutagen_inspect.py | 18 +- libs/common/mutagen/_tools/mutagen_pony.py | 11 +- libs/common/mutagen/_util.py | 173 +- libs/common/mutagen/_vorbis.py | 46 +- libs/common/mutagen/aac.py | 17 +- libs/common/mutagen/ac3.py | 329 +++ libs/common/mutagen/aiff.py | 279 +- libs/common/mutagen/apev2.py | 89 +- libs/common/mutagen/asf/__init__.py | 21 +- libs/common/mutagen/asf/_attrs.py | 43 +- libs/common/mutagen/asf/_objects.py | 35 +- libs/common/mutagen/asf/_util.py | 1 - libs/common/mutagen/dsdiff.py | 266 ++ libs/common/mutagen/dsf.py | 13 +- libs/common/mutagen/easyid3.py | 34 +- libs/common/mutagen/easymp4.py | 32 +- libs/common/mutagen/flac.py | 77 +- libs/common/mutagen/id3/__init__.py | 3 +- libs/common/mutagen/id3/_file.py | 42 +- libs/common/mutagen/id3/_frames.py | 61 +- libs/common/mutagen/id3/_id3v1.py | 85 +- libs/common/mutagen/id3/_specs.py | 62 +- libs/common/mutagen/id3/_tags.py | 87 +- libs/common/mutagen/id3/_util.py | 17 +- libs/common/mutagen/m4a.py | 1 - libs/common/mutagen/monkeysaudio.py | 4 +- libs/common/mutagen/mp3/__init__.py | 36 +- libs/common/mutagen/mp3/_util.py | 14 +- libs/common/mutagen/mp4/__init__.py | 208 +- libs/common/mutagen/mp4/_as_entry.py | 29 +- libs/common/mutagen/mp4/_atom.py | 10 +- libs/common/mutagen/mp4/_util.py | 1 - libs/common/mutagen/musepack.py | 18 +- libs/common/mutagen/ogg.py | 50 +- libs/common/mutagen/oggflac.py | 8 +- libs/common/mutagen/oggopus.py | 3 +- libs/common/mutagen/oggspeex.py | 1 - libs/common/mutagen/oggtheora.py | 17 +- libs/common/mutagen/oggvorbis.py | 12 +- libs/common/mutagen/optimfrog.py | 31 +- libs/common/mutagen/py.typed | 0 libs/common/mutagen/smf.py | 6 +- libs/common/mutagen/tak.py | 237 ++ libs/common/mutagen/trueaudio.py | 6 +- libs/common/mutagen/wave.py | 209 ++ libs/common/mutagen/wavpack.py | 21 +- libs/common/share/man/man1/mid3cp.1 | 66 + libs/common/share/man/man1/mid3iconv.1 | 68 + libs/common/share/man/man1/mid3v2.1 | 151 ++ libs/common/share/man/man1/moggsplit.1 | 64 + libs/common/share/man/man1/mutagen-inspect.1 | 47 + libs/common/share/man/man1/mutagen-pony.1 | 46 + libs/common/unidecode/__init__.py | 145 +- libs/common/unidecode/__init__.pyi | 11 + libs/common/unidecode/__main__.py | 3 + libs/common/unidecode/py.typed | 0 libs/common/unidecode/util.py | 51 +- libs/common/unidecode/x000.py | 6 +- libs/common/unidecode/x002.py | 36 +- libs/common/unidecode/x003.py | 100 +- libs/common/unidecode/x004.py | 34 +- libs/common/unidecode/x005.py | 184 +- libs/common/unidecode/x006.py | 98 +- libs/common/unidecode/x007.py | 270 +- libs/common/unidecode/x009.py | 124 +- libs/common/unidecode/x00a.py | 204 +- libs/common/unidecode/x00b.py | 226 +- libs/common/unidecode/x00c.py | 190 +- libs/common/unidecode/x00d.py | 192 +- libs/common/unidecode/x00e.py | 206 +- libs/common/unidecode/x00f.py | 126 +- libs/common/unidecode/x010.py | 198 +- libs/common/unidecode/x011.py | 30 +- libs/common/unidecode/x012.py | 50 +- libs/common/unidecode/x013.py | 114 +- libs/common/unidecode/x014.py | 4 +- libs/common/unidecode/x016.py | 52 +- libs/common/unidecode/x017.py | 304 +-- libs/common/unidecode/x018.py | 202 +- libs/common/unidecode/x01e.py | 16 +- libs/common/unidecode/x01f.py | 44 +- libs/common/unidecode/x020.py | 144 +- libs/common/unidecode/x021.py | 68 +- libs/common/unidecode/x022.py | 488 ++-- libs/common/unidecode/x023.py | 504 ++-- libs/common/unidecode/x024.py | 92 +- libs/common/unidecode/x025.py | 34 +- libs/common/unidecode/x026.py | 292 +- libs/common/unidecode/x027.py | 136 +- libs/common/unidecode/x029.py | 506 ++-- libs/common/unidecode/x02a.py | 504 ++-- libs/common/unidecode/x02c.py | 480 ++-- libs/common/unidecode/x02e.py | 510 ++-- libs/common/unidecode/x02f.py | 510 ++-- libs/common/unidecode/x030.py | 20 +- libs/common/unidecode/x031.py | 162 +- libs/common/unidecode/x032.py | 38 +- libs/common/unidecode/x04d.py | 510 ++-- libs/common/unidecode/x04e.py | 16 +- libs/common/unidecode/x04f.py | 10 +- libs/common/unidecode/x050.py | 6 +- libs/common/unidecode/x051.py | 4 +- libs/common/unidecode/x053.py | 8 +- libs/common/unidecode/x054.py | 6 +- libs/common/unidecode/x055.py | 4 +- libs/common/unidecode/x056.py | 8 +- libs/common/unidecode/x057.py | 4 +- libs/common/unidecode/x058.py | 14 +- libs/common/unidecode/x059.py | 4 +- libs/common/unidecode/x05a.py | 4 +- libs/common/unidecode/x05b.py | 2 +- libs/common/unidecode/x05c.py | 10 +- libs/common/unidecode/x05d.py | 10 +- libs/common/unidecode/x05e.py | 6 +- libs/common/unidecode/x05f.py | 4 +- libs/common/unidecode/x060.py | 2 +- libs/common/unidecode/x061.py | 6 +- libs/common/unidecode/x062.py | 4 +- libs/common/unidecode/x063.py | 8 +- libs/common/unidecode/x064.py | 4 +- libs/common/unidecode/x065.py | 4 +- libs/common/unidecode/x066.py | 2 +- libs/common/unidecode/x067.py | 8 +- libs/common/unidecode/x068.py | 4 +- libs/common/unidecode/x069.py | 8 +- libs/common/unidecode/x06a.py | 12 +- libs/common/unidecode/x06b.py | 8 +- libs/common/unidecode/x06c.py | 6 +- libs/common/unidecode/x06d.py | 4 +- libs/common/unidecode/x06e.py | 10 +- libs/common/unidecode/x06f.py | 12 +- libs/common/unidecode/x070.py | 16 +- libs/common/unidecode/x071.py | 26 +- libs/common/unidecode/x072.py | 14 +- libs/common/unidecode/x073.py | 4 +- libs/common/unidecode/x074.py | 10 +- libs/common/unidecode/x076.py | 4 +- libs/common/unidecode/x077.py | 2 +- libs/common/unidecode/x078.py | 18 +- libs/common/unidecode/x079.py | 12 +- libs/common/unidecode/x07a.py | 10 +- libs/common/unidecode/x07b.py | 2 +- libs/common/unidecode/x07c.py | 16 +- libs/common/unidecode/x07e.py | 2 +- libs/common/unidecode/x07f.py | 6 +- libs/common/unidecode/x080.py | 8 +- libs/common/unidecode/x081.py | 2 +- libs/common/unidecode/x082.py | 8 +- libs/common/unidecode/x083.py | 8 +- libs/common/unidecode/x084.py | 16 +- libs/common/unidecode/x085.py | 8 +- libs/common/unidecode/x086.py | 6 +- libs/common/unidecode/x087.py | 8 +- libs/common/unidecode/x088.py | 6 +- libs/common/unidecode/x089.py | 4 +- libs/common/unidecode/x08b.py | 4 +- libs/common/unidecode/x08c.py | 2 +- libs/common/unidecode/x08d.py | 6 +- libs/common/unidecode/x08e.py | 4 +- libs/common/unidecode/x08f.py | 2 +- libs/common/unidecode/x090.py | 4 +- libs/common/unidecode/x091.py | 2 +- libs/common/unidecode/x092.py | 4 +- libs/common/unidecode/x093.py | 20 +- libs/common/unidecode/x094.py | 8 +- libs/common/unidecode/x095.py | 4 +- libs/common/unidecode/x096.py | 2 +- libs/common/unidecode/x097.py | 10 +- libs/common/unidecode/x098.py | 2 +- libs/common/unidecode/x099.py | 2 +- libs/common/unidecode/x09b.py | 2 +- libs/common/unidecode/x09d.py | 2 +- libs/common/unidecode/x09e.py | 2 +- libs/common/unidecode/x09f.py | 178 +- libs/common/unidecode/x0a4.py | 128 +- libs/common/unidecode/x0d7.py | 182 +- libs/common/unidecode/x0fa.py | 444 +-- libs/common/unidecode/x0fb.py | 154 +- libs/common/unidecode/x0fd.py | 122 +- libs/common/unidecode/x0fe.py | 112 +- libs/common/unidecode/x0ff.py | 52 +- libs/common/unidecode/x1d4.py | 24 +- libs/common/unidecode/x1d5.py | 24 +- libs/common/unidecode/x1d6.py | 78 +- libs/common/unidecode/x1d7.py | 412 +-- libs/common/unidecode/x1f1.py | 438 +-- libs/common/unidecode/x1f6.py | 258 ++ libs/common/yaml/__init__.py | 114 +- libs/common/yaml/_yaml.cp37-win_amd64.pyd | Bin 0 -> 264192 bytes libs/common/yaml/composer.py | 4 +- libs/common/yaml/constructor.py | 182 +- libs/common/yaml/cyaml.py | 40 +- libs/common/yaml/dumper.py | 18 +- libs/common/yaml/emitter.py | 16 +- libs/common/yaml/loader.py | 25 +- libs/common/yaml/reader.py | 9 +- libs/common/yaml/representer.py | 20 +- libs/common/yaml/resolver.py | 6 +- libs/common/yaml/scanner.py | 45 +- 385 files changed, 25143 insertions(+), 18080 deletions(-) create mode 100644 libs/common/_yaml/__init__.py create mode 100644 libs/common/beets/random.py create mode 100644 libs/common/beetsplug/albumtypes.py create mode 100644 libs/common/beetsplug/aura.py create mode 100644 libs/common/beetsplug/bareasc.py create mode 100644 libs/common/beetsplug/bpsync.py delete mode 100644 libs/common/beetsplug/cue.py create mode 100644 libs/common/beetsplug/deezer.py create mode 100644 libs/common/beetsplug/fish.py create mode 100644 libs/common/beetsplug/loadext.py create mode 100644 libs/common/beetsplug/parentwork.py create mode 100644 libs/common/beetsplug/playlist.py create mode 100644 libs/common/beetsplug/subsonicplaylist.py create mode 100644 libs/common/beetsplug/subsonicupdate.py create mode 100644 libs/common/beetsplug/unimported.py delete mode 100644 libs/common/bin/chardetect.exe delete mode 100644 libs/common/bin/easy_install-3.7.exe delete mode 100644 libs/common/bin/easy_install.exe delete mode 100644 libs/common/bin/guessit.exe delete mode 100644 libs/common/bin/mid3cp create mode 100644 libs/common/bin/mid3cp.exe delete mode 100644 libs/common/bin/mid3iconv create mode 100644 libs/common/bin/mid3iconv.exe delete mode 100644 libs/common/bin/mid3v2 create mode 100644 libs/common/bin/mid3v2.exe delete mode 100644 libs/common/bin/moggsplit create mode 100644 libs/common/bin/moggsplit.exe delete mode 100644 libs/common/bin/mutagen-inspect create mode 100644 libs/common/bin/mutagen-inspect.exe delete mode 100644 libs/common/bin/mutagen-pony create mode 100644 libs/common/bin/mutagen-pony.exe delete mode 100644 libs/common/bin/pbr.exe delete mode 100644 libs/common/bin/srt.exe delete mode 100644 libs/common/bin/subliminal.exe create mode 100644 libs/common/colorama/tests/__init__.py create mode 100644 libs/common/colorama/tests/ansi_test.py create mode 100644 libs/common/colorama/tests/ansitowin32_test.py create mode 100644 libs/common/colorama/tests/initialise_test.py create mode 100644 libs/common/colorama/tests/isatty_test.py create mode 100644 libs/common/colorama/tests/utils.py create mode 100644 libs/common/colorama/tests/winterm_test.py create mode 100644 libs/common/confuse/__init__.py create mode 100644 libs/common/confuse/core.py create mode 100644 libs/common/confuse/exceptions.py create mode 100644 libs/common/confuse/sources.py create mode 100644 libs/common/confuse/templates.py create mode 100644 libs/common/confuse/util.py create mode 100644 libs/common/confuse/yaml_util.py create mode 100644 libs/common/jellyfish/__init__.pyi create mode 100644 libs/common/jellyfish/cjellyfish.cp37-win_amd64.pyd delete mode 100644 libs/common/jellyfish/compat.py create mode 100644 libs/common/jellyfish/py.typed create mode 100644 libs/common/mediafile.py delete mode 100644 libs/common/mutagen/_compat.py create mode 100644 libs/common/mutagen/_iff.py create mode 100644 libs/common/mutagen/_riff.py delete mode 100644 libs/common/mutagen/_senf/__init__.py delete mode 100644 libs/common/mutagen/_senf/_argv.py delete mode 100644 libs/common/mutagen/_senf/_compat.py delete mode 100644 libs/common/mutagen/_senf/_environ.py delete mode 100644 libs/common/mutagen/_senf/_fsnative.py delete mode 100644 libs/common/mutagen/_senf/_print.py delete mode 100644 libs/common/mutagen/_senf/_stdlib.py delete mode 100644 libs/common/mutagen/_senf/_temp.py delete mode 100644 libs/common/mutagen/_senf/_winansi.py delete mode 100644 libs/common/mutagen/_senf/_winapi.py create mode 100644 libs/common/mutagen/ac3.py create mode 100644 libs/common/mutagen/dsdiff.py create mode 100644 libs/common/mutagen/py.typed create mode 100644 libs/common/mutagen/tak.py create mode 100644 libs/common/mutagen/wave.py create mode 100644 libs/common/share/man/man1/mid3cp.1 create mode 100644 libs/common/share/man/man1/mid3iconv.1 create mode 100644 libs/common/share/man/man1/mid3v2.1 create mode 100644 libs/common/share/man/man1/moggsplit.1 create mode 100644 libs/common/share/man/man1/mutagen-inspect.1 create mode 100644 libs/common/share/man/man1/mutagen-pony.1 create mode 100644 libs/common/unidecode/__init__.pyi create mode 100644 libs/common/unidecode/__main__.py create mode 100644 libs/common/unidecode/py.typed create mode 100644 libs/common/unidecode/x1f6.py create mode 100644 libs/common/yaml/_yaml.cp37-win_amd64.pyd diff --git a/libs/common/_yaml/__init__.py b/libs/common/_yaml/__init__.py new file mode 100644 index 00000000..7baa8c4b --- /dev/null +++ b/libs/common/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/libs/common/beets/__init__.py b/libs/common/beets/__init__.py index b8fe2a84..9642a6f3 100644 --- a/libs/common/beets/__init__.py +++ b/libs/common/beets/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -13,30 +12,29 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function -import os +import confuse +from sys import stderr -from beets.util import confit - -__version__ = u'1.4.7' -__author__ = u'Adrian Sampson ' +__version__ = '1.6.0' +__author__ = 'Adrian Sampson ' -class IncludeLazyConfig(confit.LazyConfig): - """A version of Confit's LazyConfig that also merges in data from +class IncludeLazyConfig(confuse.LazyConfig): + """A version of Confuse's LazyConfig that also merges in data from YAML files specified in an `include` setting. """ def read(self, user=True, defaults=True): - super(IncludeLazyConfig, self).read(user, defaults) + super().read(user, defaults) try: for view in self['include']: - filename = view.as_filename() - if os.path.isfile(filename): - self.set_file(filename) - except confit.NotFoundError: + self.set_file(view.as_filename()) + except confuse.NotFoundError: pass + except confuse.ConfigReadError as err: + stderr.write("configuration `import` failed: {}" + .format(err.reason)) config = IncludeLazyConfig('beets', __name__) diff --git a/libs/common/beets/__main__.py b/libs/common/beets/__main__.py index 8010ca0d..ac829de9 100644 --- a/libs/common/beets/__main__.py +++ b/libs/common/beets/__main__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2017, Adrian Sampson. # @@ -17,7 +16,6 @@ `python -m beets`. """ -from __future__ import division, absolute_import, print_function import sys from .ui import main diff --git a/libs/common/beets/art.py b/libs/common/beets/art.py index 979a6f72..13d5dfbd 100644 --- a/libs/common/beets/art.py +++ b/libs/common/beets/art.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -17,7 +16,6 @@ music and items' embedded album art. """ -from __future__ import division, absolute_import, print_function import subprocess import platform @@ -26,7 +24,7 @@ import os from beets.util import displayable_path, syspath, bytestring_path from beets.util.artresizer import ArtResizer -from beets import mediafile +import mediafile def mediafile_image(image_path, maxwidth=None): @@ -43,7 +41,7 @@ def get_art(log, item): try: mf = mediafile.MediaFile(syspath(item.path)) except mediafile.UnreadableFileError as exc: - log.warning(u'Could not extract art from {0}: {1}', + log.warning('Could not extract art from {0}: {1}', displayable_path(item.path), exc) return @@ -51,26 +49,27 @@ def get_art(log, item): def embed_item(log, item, imagepath, maxwidth=None, itempath=None, - compare_threshold=0, ifempty=False, as_album=False): + compare_threshold=0, ifempty=False, as_album=False, id3v23=None, + quality=0): """Embed an image into the item's media file. """ # Conditions and filters. if compare_threshold: if not check_art_similarity(log, item, imagepath, compare_threshold): - log.info(u'Image not similar; skipping.') + log.info('Image not similar; skipping.') return if ifempty and get_art(log, item): - log.info(u'media file already contained art') - return + log.info('media file already contained art') + return if maxwidth and not as_album: - imagepath = resize_image(log, imagepath, maxwidth) + imagepath = resize_image(log, imagepath, maxwidth, quality) # Get the `Image` object from the file. try: - log.debug(u'embedding {0}', displayable_path(imagepath)) + log.debug('embedding {0}', displayable_path(imagepath)) image = mediafile_image(imagepath, maxwidth) - except IOError as exc: - log.warning(u'could not read image file: {0}', exc) + except OSError as exc: + log.warning('could not read image file: {0}', exc) return # Make sure the image kind is safe (some formats only support PNG @@ -80,36 +79,39 @@ def embed_item(log, item, imagepath, maxwidth=None, itempath=None, image.mime_type) return - item.try_write(path=itempath, tags={'images': [image]}) + item.try_write(path=itempath, tags={'images': [image]}, id3v23=id3v23) -def embed_album(log, album, maxwidth=None, quiet=False, - compare_threshold=0, ifempty=False): +def embed_album(log, album, maxwidth=None, quiet=False, compare_threshold=0, + ifempty=False, quality=0): """Embed album art into all of the album's items. """ imagepath = album.artpath if not imagepath: - log.info(u'No album art present for {0}', album) + log.info('No album art present for {0}', album) return if not os.path.isfile(syspath(imagepath)): - log.info(u'Album art not found at {0} for {1}', + log.info('Album art not found at {0} for {1}', displayable_path(imagepath), album) return if maxwidth: - imagepath = resize_image(log, imagepath, maxwidth) + imagepath = resize_image(log, imagepath, maxwidth, quality) - log.info(u'Embedding album art into {0}', album) + log.info('Embedding album art into {0}', album) for item in album.items(): - embed_item(log, item, imagepath, maxwidth, None, - compare_threshold, ifempty, as_album=True) + embed_item(log, item, imagepath, maxwidth, None, compare_threshold, + ifempty, as_album=True, quality=quality) -def resize_image(log, imagepath, maxwidth): - """Returns path to an image resized to maxwidth. +def resize_image(log, imagepath, maxwidth, quality): + """Returns path to an image resized to maxwidth and encoded with the + specified quality level. """ - log.debug(u'Resizing album art to {0} pixels wide', maxwidth) - imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath)) + log.debug('Resizing album art to {0} pixels wide and encoding at quality \ + level {1}', maxwidth, quality) + imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath), + quality=quality) return imagepath @@ -131,7 +133,7 @@ def check_art_similarity(log, item, imagepath, compare_threshold): syspath(art, prefix=False), '-colorspace', 'gray', 'MIFF:-'] compare_cmd = ['compare', '-metric', 'PHASH', '-', 'null:'] - log.debug(u'comparing images with pipeline {} | {}', + log.debug('comparing images with pipeline {} | {}', convert_cmd, compare_cmd) convert_proc = subprocess.Popen( convert_cmd, @@ -155,7 +157,7 @@ def check_art_similarity(log, item, imagepath, compare_threshold): convert_proc.wait() if convert_proc.returncode: log.debug( - u'ImageMagick convert failed with status {}: {!r}', + 'ImageMagick convert failed with status {}: {!r}', convert_proc.returncode, convert_stderr, ) @@ -165,7 +167,7 @@ def check_art_similarity(log, item, imagepath, compare_threshold): stdout, stderr = compare_proc.communicate() if compare_proc.returncode: if compare_proc.returncode != 1: - log.debug(u'ImageMagick compare failed: {0}, {1}', + log.debug('ImageMagick compare failed: {0}, {1}', displayable_path(imagepath), displayable_path(art)) return @@ -176,10 +178,10 @@ def check_art_similarity(log, item, imagepath, compare_threshold): try: phash_diff = float(out_str) except ValueError: - log.debug(u'IM output is not a number: {0!r}', out_str) + log.debug('IM output is not a number: {0!r}', out_str) return - log.debug(u'ImageMagick compare score: {0}', phash_diff) + log.debug('ImageMagick compare score: {0}', phash_diff) return phash_diff <= compare_threshold return True @@ -189,18 +191,18 @@ def extract(log, outpath, item): art = get_art(log, item) outpath = bytestring_path(outpath) if not art: - log.info(u'No album art present in {0}, skipping.', item) + log.info('No album art present in {0}, skipping.', item) return # Add an extension to the filename. ext = mediafile.image_extension(art) if not ext: - log.warning(u'Unknown image type in {0}.', + log.warning('Unknown image type in {0}.', displayable_path(item.path)) return outpath += bytestring_path('.' + ext) - log.info(u'Extracting album art from: {0} to: {1}', + log.info('Extracting album art from: {0} to: {1}', item, displayable_path(outpath)) with open(syspath(outpath), 'wb') as f: f.write(art) @@ -216,7 +218,7 @@ def extract_first(log, outpath, items): def clear(log, lib, query): items = lib.items(query) - log.info(u'Clearing album art from {0} items', len(items)) + log.info('Clearing album art from {0} items', len(items)) for item in items: - log.debug(u'Clearing art for {0}', item) + log.debug('Clearing art for {0}', item) item.try_write(tags={'images': None}) diff --git a/libs/common/beets/autotag/__init__.py b/libs/common/beets/autotag/__init__.py index c4ee1300..e62f492c 100644 --- a/libs/common/beets/autotag/__init__.py +++ b/libs/common/beets/autotag/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,19 +15,59 @@ """Facilities for automatically determining files' correct metadata. """ -from __future__ import division, absolute_import, print_function from beets import logging from beets import config # Parts of external interface. -from .hooks import AlbumInfo, TrackInfo, AlbumMatch, TrackMatch # noqa +from .hooks import ( # noqa + AlbumInfo, + TrackInfo, + AlbumMatch, + TrackMatch, + Distance, +) from .match import tag_item, tag_album, Proposal # noqa from .match import Recommendation # noqa # Global logger. log = logging.getLogger('beets') +# Metadata fields that are already hardcoded, or where the tag name changes. +SPECIAL_FIELDS = { + 'album': ( + 'va', + 'releasegroup_id', + 'artist_id', + 'album_id', + 'mediums', + 'tracks', + 'year', + 'month', + 'day', + 'artist', + 'artist_credit', + 'artist_sort', + 'data_url' + ), + 'track': ( + 'track_alt', + 'artist_id', + 'release_track_id', + 'medium', + 'index', + 'medium_index', + 'title', + 'artist_credit', + 'artist_sort', + 'artist', + 'track_id', + 'medium_total', + 'data_url', + 'length' + ) +} + # Additional utilities for the main interface. @@ -43,17 +82,14 @@ def apply_item_metadata(item, track_info): item.mb_releasetrackid = track_info.release_track_id if track_info.artist_id: item.mb_artistid = track_info.artist_id - if track_info.data_source: - item.data_source = track_info.data_source - if track_info.lyricist is not None: - item.lyricist = track_info.lyricist - if track_info.composer is not None: - item.composer = track_info.composer - if track_info.composer_sort is not None: - item.composer_sort = track_info.composer_sort - if track_info.arranger is not None: - item.arranger = track_info.arranger + for field, value in track_info.items(): + # We only overwrite fields that are not already hardcoded. + if field in SPECIAL_FIELDS['track']: + continue + if value is None: + continue + item[field] = value # At the moment, the other metadata is left intact (including album # and track number). Perhaps these should be emptied? @@ -142,33 +178,24 @@ def apply_metadata(album_info, mapping): # Compilation flag. item.comp = album_info.va - # Miscellaneous metadata. - for field in ('albumtype', - 'label', - 'asin', - 'catalognum', - 'script', - 'language', - 'country', - 'albumstatus', - 'albumdisambig', - 'data_source',): - value = getattr(album_info, field) - if value is not None: - item[field] = value - if track_info.disctitle is not None: - item.disctitle = track_info.disctitle - - if track_info.media is not None: - item.media = track_info.media - - if track_info.lyricist is not None: - item.lyricist = track_info.lyricist - if track_info.composer is not None: - item.composer = track_info.composer - if track_info.composer_sort is not None: - item.composer_sort = track_info.composer_sort - if track_info.arranger is not None: - item.arranger = track_info.arranger - + # Track alt. item.track_alt = track_info.track_alt + + # Don't overwrite fields with empty values unless the + # field is explicitly allowed to be overwritten + for field, value in album_info.items(): + if field in SPECIAL_FIELDS['album']: + continue + clobber = field in config['overwrite_null']['album'].as_str_seq() + if value is None and not clobber: + continue + item[field] = value + + for field, value in track_info.items(): + if field in SPECIAL_FIELDS['track']: + continue + clobber = field in config['overwrite_null']['track'].as_str_seq() + value = getattr(track_info, field) + if value is None and not clobber: + continue + item[field] = value diff --git a/libs/common/beets/autotag/hooks.py b/libs/common/beets/autotag/hooks.py index 3615a933..9cd6f2cd 100644 --- a/libs/common/beets/autotag/hooks.py +++ b/libs/common/beets/autotag/hooks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -14,7 +13,6 @@ # included in all copies or substantial portions of the Software. """Glue between metadata sources and the matching logic.""" -from __future__ import division, absolute_import, print_function from collections import namedtuple from functools import total_ordering @@ -27,14 +25,36 @@ from beets.util import as_string from beets.autotag import mb from jellyfish import levenshtein_distance from unidecode import unidecode -import six log = logging.getLogger('beets') +# The name of the type for patterns in re changed in Python 3.7. +try: + Pattern = re._pattern_type +except AttributeError: + Pattern = re.Pattern + # Classes used to represent candidate options. +class AttrDict(dict): + """A dictionary that supports attribute ("dot") access, so `d.field` + is equivalent to `d['field']`. + """ -class AlbumInfo(object): + def __getattr__(self, attr): + if attr in self: + return self.get(attr) + else: + raise AttributeError + + def __setattr__(self, key, value): + self.__setitem__(key, value) + + def __hash__(self): + return id(self) + + +class AlbumInfo(AttrDict): """Describes a canonical release that may be used to match a release in the library. Consists of these data members: @@ -43,38 +63,22 @@ class AlbumInfo(object): - ``artist``: name of the release's primary artist - ``artist_id`` - ``tracks``: list of TrackInfo objects making up the release - - ``asin``: Amazon ASIN - - ``albumtype``: string describing the kind of release - - ``va``: boolean: whether the release has "various artists" - - ``year``: release year - - ``month``: release month - - ``day``: release day - - ``label``: music label responsible for the release - - ``mediums``: the number of discs in this release - - ``artist_sort``: name of the release's artist for sorting - - ``releasegroup_id``: MBID for the album's release group - - ``catalognum``: the label's catalog number for the release - - ``script``: character set used for metadata - - ``language``: human language of the metadata - - ``country``: the release country - - ``albumstatus``: MusicBrainz release status (Official, etc.) - - ``media``: delivery mechanism (Vinyl, etc.) - - ``albumdisambig``: MusicBrainz release disambiguation comment - - ``artist_credit``: Release-specific artist name - - ``data_source``: The original data source (MusicBrainz, Discogs, etc.) - - ``data_url``: The data source release URL. - The fields up through ``tracks`` are required. The others are - optional and may be None. + ``mediums`` along with the fields up through ``tracks`` are required. + The others are optional and may be None. """ - def __init__(self, album, album_id, artist, artist_id, tracks, asin=None, - albumtype=None, va=False, year=None, month=None, day=None, - label=None, mediums=None, artist_sort=None, - releasegroup_id=None, catalognum=None, script=None, - language=None, country=None, albumstatus=None, media=None, - albumdisambig=None, artist_credit=None, original_year=None, - original_month=None, original_day=None, data_source=None, - data_url=None): + + def __init__(self, tracks, album=None, album_id=None, artist=None, + artist_id=None, asin=None, albumtype=None, va=False, + year=None, month=None, day=None, label=None, mediums=None, + artist_sort=None, releasegroup_id=None, catalognum=None, + script=None, language=None, country=None, style=None, + genre=None, albumstatus=None, media=None, albumdisambig=None, + releasegroupdisambig=None, artist_credit=None, + original_year=None, original_month=None, + original_day=None, data_source=None, data_url=None, + discogs_albumid=None, discogs_labelid=None, + discogs_artistid=None, **kwargs): self.album = album self.album_id = album_id self.artist = artist @@ -94,15 +98,22 @@ class AlbumInfo(object): self.script = script self.language = language self.country = country + self.style = style + self.genre = genre self.albumstatus = albumstatus self.media = media self.albumdisambig = albumdisambig + self.releasegroupdisambig = releasegroupdisambig self.artist_credit = artist_credit self.original_year = original_year self.original_month = original_month self.original_day = original_day self.data_source = data_source self.data_url = data_url + self.discogs_albumid = discogs_albumid + self.discogs_labelid = discogs_labelid + self.discogs_artistid = discogs_artistid + self.update(kwargs) # Work around a bug in python-musicbrainz-ngs that causes some # strings to be bytes rather than Unicode. @@ -112,54 +123,46 @@ class AlbumInfo(object): constituent `TrackInfo` objects, are decoded to Unicode. """ for fld in ['album', 'artist', 'albumtype', 'label', 'artist_sort', - 'catalognum', 'script', 'language', 'country', - 'albumstatus', 'albumdisambig', 'artist_credit', 'media']: + 'catalognum', 'script', 'language', 'country', 'style', + 'genre', 'albumstatus', 'albumdisambig', + 'releasegroupdisambig', 'artist_credit', + 'media', 'discogs_albumid', 'discogs_labelid', + 'discogs_artistid']: value = getattr(self, fld) if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) - if self.tracks: - for track in self.tracks: - track.decode(codec) + for track in self.tracks: + track.decode(codec) + + def copy(self): + dupe = AlbumInfo([]) + dupe.update(self) + dupe.tracks = [track.copy() for track in self.tracks] + return dupe -class TrackInfo(object): +class TrackInfo(AttrDict): """Describes a canonical track present on a release. Appears as part of an AlbumInfo's ``tracks`` list. Consists of these data members: - ``title``: name of the track - ``track_id``: MusicBrainz ID; UUID fragment only - - ``release_track_id``: MusicBrainz ID respective to a track on a - particular release; UUID fragment only - - ``artist``: individual track artist name - - ``artist_id`` - - ``length``: float: duration of the track in seconds - - ``index``: position on the entire release - - ``media``: delivery mechanism (Vinyl, etc.) - - ``medium``: the disc number this track appears on in the album - - ``medium_index``: the track's position on the disc - - ``medium_total``: the number of tracks on the item's disc - - ``artist_sort``: name of the track artist for sorting - - ``disctitle``: name of the individual medium (subtitle) - - ``artist_credit``: Recording-specific artist name - - ``data_source``: The original data source (MusicBrainz, Discogs, etc.) - - ``data_url``: The data source release URL. - - ``lyricist``: individual track lyricist name - - ``composer``: individual track composer name - - ``composer_sort``: individual track composer sort name - - ``arranger`: individual track arranger name - - ``track_alt``: alternative track number (tape, vinyl, etc.) Only ``title`` and ``track_id`` are required. The rest of the fields may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id, release_track_id=None, artist=None, - artist_id=None, length=None, index=None, medium=None, - medium_index=None, medium_total=None, artist_sort=None, - disctitle=None, artist_credit=None, data_source=None, - data_url=None, media=None, lyricist=None, composer=None, - composer_sort=None, arranger=None, track_alt=None): + + def __init__(self, title=None, track_id=None, release_track_id=None, + artist=None, artist_id=None, length=None, index=None, + medium=None, medium_index=None, medium_total=None, + artist_sort=None, disctitle=None, artist_credit=None, + data_source=None, data_url=None, media=None, lyricist=None, + composer=None, composer_sort=None, arranger=None, + track_alt=None, work=None, mb_workid=None, + work_disambig=None, bpm=None, initial_key=None, genre=None, + **kwargs): self.title = title self.track_id = track_id self.release_track_id = release_track_id @@ -181,6 +184,13 @@ class TrackInfo(object): self.composer_sort = composer_sort self.arranger = arranger self.track_alt = track_alt + self.work = work + self.mb_workid = mb_workid + self.work_disambig = work_disambig + self.bpm = bpm + self.initial_key = initial_key + self.genre = genre + self.update(kwargs) # As above, work around a bug in python-musicbrainz-ngs. def decode(self, codec='utf-8'): @@ -193,6 +203,11 @@ class TrackInfo(object): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) + def copy(self): + dupe = TrackInfo() + dupe.update(self) + return dupe + # Candidate distance scoring. @@ -220,8 +235,8 @@ def _string_dist_basic(str1, str2): transliteration/lowering to ASCII characters. Normalized by string length. """ - assert isinstance(str1, six.text_type) - assert isinstance(str2, six.text_type) + assert isinstance(str1, str) + assert isinstance(str2, str) str1 = as_string(unidecode(str1)) str2 = as_string(unidecode(str2)) str1 = re.sub(r'[^a-z0-9]', '', str1.lower()) @@ -249,9 +264,9 @@ def string_dist(str1, str2): # "something, the". for word in SD_END_WORDS: if str1.endswith(', %s' % word): - str1 = '%s %s' % (word, str1[:-len(word) - 2]) + str1 = '{} {}'.format(word, str1[:-len(word) - 2]) if str2.endswith(', %s' % word): - str2 = '%s %s' % (word, str2[:-len(word) - 2]) + str2 = '{} {}'.format(word, str2[:-len(word) - 2]) # Perform a couple of basic normalizing substitutions. for pat, repl in SD_REPLACE: @@ -289,11 +304,12 @@ def string_dist(str1, str2): return base_dist + penalty -class LazyClassProperty(object): +class LazyClassProperty: """A decorator implementing a read-only property that is *lazy* in the sense that the getter is only invoked once. Subsequent accesses through *any* instance use the cached result. """ + def __init__(self, getter): self.getter = getter self.computed = False @@ -306,17 +322,17 @@ class LazyClassProperty(object): @total_ordering -@six.python_2_unicode_compatible -class Distance(object): +class Distance: """Keeps track of multiple distance penalties. Provides a single weighted distance for all penalties as well as a weighted distance for each individual penalty. """ + def __init__(self): self._penalties = {} @LazyClassProperty - def _weights(cls): # noqa + def _weights(cls): # noqa: N805 """A dictionary from keys to floating-point weights. """ weights_view = config['match']['distance_weights'] @@ -394,7 +410,7 @@ class Distance(object): return other - self.distance def __str__(self): - return "{0:.2f}".format(self.distance) + return f"{self.distance:.2f}" # Behave like a dict. @@ -421,7 +437,7 @@ class Distance(object): """ if not isinstance(dist, Distance): raise ValueError( - u'`dist` must be a Distance object, not {0}'.format(type(dist)) + '`dist` must be a Distance object, not {}'.format(type(dist)) ) for key, penalties in dist._penalties.items(): self._penalties.setdefault(key, []).extend(penalties) @@ -433,7 +449,7 @@ class Distance(object): be a compiled regular expression, in which case it will be matched against `value2`. """ - if isinstance(value1, re._pattern_type): + if isinstance(value1, Pattern): return bool(value1.match(value2)) return value1 == value2 @@ -445,7 +461,7 @@ class Distance(object): """ if not 0.0 <= dist <= 1.0: raise ValueError( - u'`dist` must be between 0.0 and 1.0, not {0}'.format(dist) + f'`dist` must be between 0.0 and 1.0, not {dist}' ) self._penalties.setdefault(key, []).append(dist) @@ -541,7 +557,7 @@ def album_for_mbid(release_id): try: album = mb.album_for_id(release_id) if album: - plugins.send(u'albuminfo_received', info=album) + plugins.send('albuminfo_received', info=album) return album except mb.MusicBrainzAPIError as exc: exc.log(log) @@ -554,7 +570,7 @@ def track_for_mbid(recording_id): try: track = mb.track_for_id(recording_id) if track: - plugins.send(u'trackinfo_received', info=track) + plugins.send('trackinfo_received', info=track) return track except mb.MusicBrainzAPIError as exc: exc.log(log) @@ -567,7 +583,7 @@ def albums_for_id(album_id): yield a for a in plugins.album_for_id(album_id): if a: - plugins.send(u'albuminfo_received', info=a) + plugins.send('albuminfo_received', info=a) yield a @@ -578,40 +594,43 @@ def tracks_for_id(track_id): yield t for t in plugins.track_for_id(track_id): if t: - plugins.send(u'trackinfo_received', info=t) + plugins.send('trackinfo_received', info=t) yield t -@plugins.notify_info_yielded(u'albuminfo_received') -def album_candidates(items, artist, album, va_likely): +@plugins.notify_info_yielded('albuminfo_received') +def album_candidates(items, artist, album, va_likely, extra_tags): """Search for album matches. ``items`` is a list of Item objects that make up the album. ``artist`` and ``album`` are the respective names (strings), which may be derived from the item list or may be entered by the user. ``va_likely`` is a boolean indicating whether - the album is likely to be a "various artists" release. + the album is likely to be a "various artists" release. ``extra_tags`` + is an optional dictionary of additional tags used to further + constrain the search. """ + # Base candidates if we have album and artist to match. if artist and album: try: - for candidate in mb.match_album(artist, album, len(items)): - yield candidate + yield from mb.match_album(artist, album, len(items), + extra_tags) except mb.MusicBrainzAPIError as exc: exc.log(log) # Also add VA matches from MusicBrainz where appropriate. if va_likely and album: try: - for candidate in mb.match_album(None, album, len(items)): - yield candidate + yield from mb.match_album(None, album, len(items), + extra_tags) except mb.MusicBrainzAPIError as exc: exc.log(log) # Candidates from plugins. - for candidate in plugins.candidates(items, artist, album, va_likely): - yield candidate + yield from plugins.candidates(items, artist, album, va_likely, + extra_tags) -@plugins.notify_info_yielded(u'trackinfo_received') +@plugins.notify_info_yielded('trackinfo_received') def item_candidates(item, artist, title): """Search for item matches. ``item`` is the Item to be matched. ``artist`` and ``title`` are strings and either reflect the item or @@ -621,11 +640,9 @@ def item_candidates(item, artist, title): # MusicBrainz candidates. if artist and title: try: - for candidate in mb.match_track(artist, title): - yield candidate + yield from mb.match_track(artist, title) except mb.MusicBrainzAPIError as exc: exc.log(log) # Plugin candidates. - for candidate in plugins.item_candidates(item, artist, title): - yield candidate + yield from plugins.item_candidates(item, artist, title) diff --git a/libs/common/beets/autotag/match.py b/libs/common/beets/autotag/match.py index 71b62adb..d352a013 100644 --- a/libs/common/beets/autotag/match.py +++ b/libs/common/beets/autotag/match.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -17,7 +16,6 @@ releases and tracks. """ -from __future__ import division, absolute_import, print_function import datetime import re @@ -35,7 +33,7 @@ from beets.util.enumeration import OrderedEnum # album level to determine whether a given release is likely a VA # release and also on the track level to to remove the penalty for # differing artists. -VA_ARTISTS = (u'', u'various artists', u'various', u'va', u'unknown') +VA_ARTISTS = ('', 'various artists', 'various', 'va', 'unknown') # Global logger. log = logging.getLogger('beets') @@ -108,7 +106,7 @@ def assign_items(items, tracks): log.debug('...done.') # Produce the output matching. - mapping = dict((items[i], tracks[j]) for (i, j) in matching) + mapping = {items[i]: tracks[j] for (i, j) in matching} extra_items = list(set(items) - set(mapping.keys())) extra_items.sort(key=lambda i: (i.disc, i.track, i.title)) extra_tracks = list(set(tracks) - set(mapping.values())) @@ -276,16 +274,16 @@ def match_by_id(items): try: first = next(albumids) except StopIteration: - log.debug(u'No album ID found.') + log.debug('No album ID found.') return None # Is there a consensus on the MB album ID? for other in albumids: if other != first: - log.debug(u'No album ID consensus.') + log.debug('No album ID consensus.') return None # If all album IDs are equal, look up the album. - log.debug(u'Searching for discovered album ID: {0}', first) + log.debug('Searching for discovered album ID: {0}', first) return hooks.album_for_mbid(first) @@ -351,23 +349,23 @@ def _add_candidate(items, results, info): checking the track count, ordering the items, checking for duplicates, and calculating the distance. """ - log.debug(u'Candidate: {0} - {1} ({2})', + log.debug('Candidate: {0} - {1} ({2})', info.artist, info.album, info.album_id) # Discard albums with zero tracks. if not info.tracks: - log.debug(u'No tracks.') + log.debug('No tracks.') return # Don't duplicate. if info.album_id in results: - log.debug(u'Duplicate.') + log.debug('Duplicate.') return # Discard matches without required tags. for req_tag in config['match']['required'].as_str_seq(): if getattr(info, req_tag) is None: - log.debug(u'Ignored. Missing required tag: {0}', req_tag) + log.debug('Ignored. Missing required tag: {0}', req_tag) return # Find mapping between the items and the track info. @@ -380,10 +378,10 @@ def _add_candidate(items, results, info): penalties = [key for key, _ in dist] for penalty in config['match']['ignored'].as_str_seq(): if penalty in penalties: - log.debug(u'Ignored. Penalty: {0}', penalty) + log.debug('Ignored. Penalty: {0}', penalty) return - log.debug(u'Success. Distance: {0}', dist) + log.debug('Success. Distance: {0}', dist) results[info.album_id] = hooks.AlbumMatch(dist, info, mapping, extra_items, extra_tracks) @@ -411,7 +409,7 @@ def tag_album(items, search_artist=None, search_album=None, likelies, consensus = current_metadata(items) cur_artist = likelies['artist'] cur_album = likelies['album'] - log.debug(u'Tagging {0} - {1}', cur_artist, cur_album) + log.debug('Tagging {0} - {1}', cur_artist, cur_album) # The output result (distance, AlbumInfo) tuples (keyed by MB album # ID). @@ -420,7 +418,7 @@ def tag_album(items, search_artist=None, search_album=None, # Search by explicit ID. if search_ids: for search_id in search_ids: - log.debug(u'Searching for album ID: {0}', search_id) + log.debug('Searching for album ID: {0}', search_id) for id_candidate in hooks.albums_for_id(search_id): _add_candidate(items, candidates, id_candidate) @@ -431,13 +429,13 @@ def tag_album(items, search_artist=None, search_album=None, if id_info: _add_candidate(items, candidates, id_info) rec = _recommendation(list(candidates.values())) - log.debug(u'Album ID match recommendation is {0}', rec) + log.debug('Album ID match recommendation is {0}', rec) if candidates and not config['import']['timid']: # If we have a very good MBID match, return immediately. # Otherwise, this match will compete against metadata-based # matches. if rec == Recommendation.strong: - log.debug(u'ID match.') + log.debug('ID match.') return cur_artist, cur_album, \ Proposal(list(candidates.values()), rec) @@ -445,22 +443,29 @@ def tag_album(items, search_artist=None, search_album=None, if not (search_artist and search_album): # No explicit search terms -- use current metadata. search_artist, search_album = cur_artist, cur_album - log.debug(u'Search terms: {0} - {1}', search_artist, search_album) + log.debug('Search terms: {0} - {1}', search_artist, search_album) + + extra_tags = None + if config['musicbrainz']['extra_tags']: + tag_list = config['musicbrainz']['extra_tags'].get() + extra_tags = {k: v for (k, v) in likelies.items() if k in tag_list} + log.debug('Additional search terms: {0}', extra_tags) # Is this album likely to be a "various artist" release? va_likely = ((not consensus['artist']) or (search_artist.lower() in VA_ARTISTS) or any(item.comp for item in items)) - log.debug(u'Album might be VA: {0}', va_likely) + log.debug('Album might be VA: {0}', va_likely) # Get the results from the data sources. for matched_candidate in hooks.album_candidates(items, search_artist, search_album, - va_likely): + va_likely, + extra_tags): _add_candidate(items, candidates, matched_candidate) - log.debug(u'Evaluating {0} candidates.', len(candidates)) + log.debug('Evaluating {0} candidates.', len(candidates)) # Sort and get the recommendation. candidates = _sort_candidates(candidates.values()) rec = _recommendation(candidates) @@ -485,7 +490,7 @@ def tag_item(item, search_artist=None, search_title=None, trackids = search_ids or [t for t in [item.mb_trackid] if t] if trackids: for trackid in trackids: - log.debug(u'Searching for track ID: {0}', trackid) + log.debug('Searching for track ID: {0}', trackid) for track_info in hooks.tracks_for_id(trackid): dist = track_distance(item, track_info, incl_artist=True) candidates[track_info.track_id] = \ @@ -494,7 +499,7 @@ def tag_item(item, search_artist=None, search_title=None, rec = _recommendation(_sort_candidates(candidates.values())) if rec == Recommendation.strong and \ not config['import']['timid']: - log.debug(u'Track ID match.') + log.debug('Track ID match.') return Proposal(_sort_candidates(candidates.values()), rec) # If we're searching by ID, don't proceed. @@ -507,7 +512,7 @@ def tag_item(item, search_artist=None, search_title=None, # Search terms. if not (search_artist and search_title): search_artist, search_title = item.artist, item.title - log.debug(u'Item search terms: {0} - {1}', search_artist, search_title) + log.debug('Item search terms: {0} - {1}', search_artist, search_title) # Get and evaluate candidate metadata. for track_info in hooks.item_candidates(item, search_artist, search_title): @@ -515,7 +520,7 @@ def tag_item(item, search_artist=None, search_title=None, candidates[track_info.track_id] = hooks.TrackMatch(dist, track_info) # Sort by distance and return with recommendation. - log.debug(u'Found {0} candidates.', len(candidates)) + log.debug('Found {0} candidates.', len(candidates)) candidates = _sort_candidates(candidates.values()) rec = _recommendation(candidates) return Proposal(candidates, rec) diff --git a/libs/common/beets/autotag/mb.py b/libs/common/beets/autotag/mb.py index 2b28a5cc..e6a2e277 100644 --- a/libs/common/beets/autotag/mb.py +++ b/libs/common/beets/autotag/mb.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,57 +14,72 @@ """Searches for albums in the MusicBrainz database. """ -from __future__ import division, absolute_import, print_function import musicbrainzngs import re import traceback -from six.moves.urllib.parse import urljoin from beets import logging +from beets import plugins import beets.autotag.hooks import beets from beets import util from beets import config -import six +from collections import Counter +from urllib.parse import urljoin VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377' -if util.SNI_SUPPORTED: - BASE_URL = 'https://musicbrainz.org/' -else: - BASE_URL = 'http://musicbrainz.org/' +BASE_URL = 'https://musicbrainz.org/' SKIPPED_TRACKS = ['[data track]'] +FIELDS_TO_MB_KEYS = { + 'catalognum': 'catno', + 'country': 'country', + 'label': 'label', + 'media': 'format', + 'year': 'date', +} + musicbrainzngs.set_useragent('beets', beets.__version__, - 'http://beets.io/') + 'https://beets.io/') class MusicBrainzAPIError(util.HumanReadableException): """An error while talking to MusicBrainz. The `query` field is the parameter to the action and may have any type. """ + def __init__(self, reason, verb, query, tb=None): self.query = query if isinstance(reason, musicbrainzngs.WebServiceError): - reason = u'MusicBrainz not reachable' - super(MusicBrainzAPIError, self).__init__(reason, verb, tb) + reason = 'MusicBrainz not reachable' + super().__init__(reason, verb, tb) def get_message(self): - return u'{0} in {1} with query {2}'.format( + return '{} in {} with query {}'.format( self._reasonstr(), self.verb, repr(self.query) ) + log = logging.getLogger('beets') RELEASE_INCLUDES = ['artists', 'media', 'recordings', 'release-groups', 'labels', 'artist-credits', 'aliases', 'recording-level-rels', 'work-rels', - 'work-level-rels', 'artist-rels'] -TRACK_INCLUDES = ['artists', 'aliases'] + 'work-level-rels', 'artist-rels', 'isrcs'] +BROWSE_INCLUDES = ['artist-credits', 'work-rels', + 'artist-rels', 'recording-rels', 'release-rels'] +if "work-level-rels" in musicbrainzngs.VALID_BROWSE_INCLUDES['recording']: + BROWSE_INCLUDES.append("work-level-rels") +BROWSE_CHUNKSIZE = 100 +BROWSE_MAXTRACKS = 500 +TRACK_INCLUDES = ['artists', 'aliases', 'isrcs'] if 'work-level-rels' in musicbrainzngs.VALID_INCLUDES['recording']: TRACK_INCLUDES += ['work-level-rels', 'artist-rels'] +if 'genres' in musicbrainzngs.VALID_INCLUDES['recording']: + RELEASE_INCLUDES += ['genres'] def track_url(trackid): @@ -81,7 +95,11 @@ def configure(): from the beets configuration. This should be called at startup. """ hostname = config['musicbrainz']['host'].as_str() - musicbrainzngs.set_hostname(hostname) + https = config['musicbrainz']['https'].get(bool) + # Only call set_hostname when a custom server is configured. Since + # musicbrainz-ngs connects to musicbrainz.org with HTTPS by default + if hostname != "musicbrainz.org": + musicbrainzngs.set_hostname(hostname, https) musicbrainzngs.set_rate_limit( config['musicbrainz']['ratelimit_interval'].as_number(), config['musicbrainz']['ratelimit'].get(int), @@ -138,7 +156,7 @@ def _flatten_artist_credit(credit): artist_sort_parts = [] artist_credit_parts = [] for el in credit: - if isinstance(el, six.string_types): + if isinstance(el, str): # Join phrase. artist_parts.append(el) artist_credit_parts.append(el) @@ -185,13 +203,13 @@ def track_info(recording, index=None, medium=None, medium_index=None, the number of tracks on the medium. Each number is a 1-based index. """ info = beets.autotag.hooks.TrackInfo( - recording['title'], - recording['id'], + title=recording['title'], + track_id=recording['id'], index=index, medium=medium, medium_index=medium_index, medium_total=medium_total, - data_source=u'MusicBrainz', + data_source='MusicBrainz', data_url=track_url(recording['id']), ) @@ -207,12 +225,22 @@ def track_info(recording, index=None, medium=None, medium_index=None, if recording.get('length'): info.length = int(recording['length']) / (1000.0) + info.trackdisambig = recording.get('disambiguation') + + if recording.get('isrc-list'): + info.isrc = ';'.join(recording['isrc-list']) + lyricist = [] composer = [] composer_sort = [] for work_relation in recording.get('work-relation-list', ()): if work_relation['type'] != 'performance': continue + info.work = work_relation['work']['title'] + info.mb_workid = work_relation['work']['id'] + if 'disambiguation' in work_relation['work']: + info.work_disambig = work_relation['work']['disambiguation'] + for artist_relation in work_relation['work'].get( 'artist-relation-list', ()): if 'type' in artist_relation: @@ -224,10 +252,10 @@ def track_info(recording, index=None, medium=None, medium_index=None, composer_sort.append( artist_relation['artist']['sort-name']) if lyricist: - info.lyricist = u', '.join(lyricist) + info.lyricist = ', '.join(lyricist) if composer: - info.composer = u', '.join(composer) - info.composer_sort = u', '.join(composer_sort) + info.composer = ', '.join(composer) + info.composer_sort = ', '.join(composer_sort) arranger = [] for artist_relation in recording.get('artist-relation-list', ()): @@ -236,7 +264,12 @@ def track_info(recording, index=None, medium=None, medium_index=None, if type == 'arranger': arranger.append(artist_relation['artist']['name']) if arranger: - info.arranger = u', '.join(arranger) + info.arranger = ', '.join(arranger) + + # Supplementary fields provided by plugins + extra_trackdatas = plugins.send('mb_track_extract', data=recording) + for extra_trackdata in extra_trackdatas: + info.update(extra_trackdata) info.decode() return info @@ -270,6 +303,26 @@ def album_info(release): artist_name, artist_sort_name, artist_credit_name = \ _flatten_artist_credit(release['artist-credit']) + ntracks = sum(len(m['track-list']) for m in release['medium-list']) + + # The MusicBrainz API omits 'artist-relation-list' and 'work-relation-list' + # when the release has more than 500 tracks. So we use browse_recordings + # on chunks of tracks to recover the same information in this case. + if ntracks > BROWSE_MAXTRACKS: + log.debug('Album {} has too many tracks', release['id']) + recording_list = [] + for i in range(0, ntracks, BROWSE_CHUNKSIZE): + log.debug('Retrieving tracks starting at {}', i) + recording_list.extend(musicbrainzngs.browse_recordings( + release=release['id'], limit=BROWSE_CHUNKSIZE, + includes=BROWSE_INCLUDES, + offset=i)['recording-list']) + track_map = {r['id']: r for r in recording_list} + for medium in release['medium-list']: + for recording in medium['track-list']: + recording_info = track_map[recording['recording']['id']] + recording['recording'] = recording_info + # Basic info. track_infos = [] index = 0 @@ -281,7 +334,8 @@ def album_info(release): continue all_tracks = medium['track-list'] - if 'data-track-list' in medium: + if ('data-track-list' in medium + and not config['match']['ignore_data_tracks']): all_tracks += medium['data-track-list'] track_count = len(all_tracks) @@ -327,15 +381,15 @@ def album_info(release): track_infos.append(ti) info = beets.autotag.hooks.AlbumInfo( - release['title'], - release['id'], - artist_name, - release['artist-credit'][0]['artist']['id'], - track_infos, + album=release['title'], + album_id=release['id'], + artist=artist_name, + artist_id=release['artist-credit'][0]['artist']['id'], + tracks=track_infos, mediums=len(release['medium-list']), artist_sort=artist_sort_name, artist_credit=artist_credit_name, - data_source=u'MusicBrainz', + data_source='MusicBrainz', data_url=album_url(release['id']), ) info.va = info.artist_id == VARIOUS_ARTISTS_ID @@ -345,13 +399,12 @@ def album_info(release): info.releasegroup_id = release['release-group']['id'] info.albumstatus = release.get('status') - # Build up the disambiguation string from the release group and release. - disambig = [] + # Get the disambiguation strings at the release and release group level. if release['release-group'].get('disambiguation'): - disambig.append(release['release-group'].get('disambiguation')) + info.releasegroupdisambig = \ + release['release-group'].get('disambiguation') if release.get('disambiguation'): - disambig.append(release.get('disambiguation')) - info.albumdisambig = u', '.join(disambig) + info.albumdisambig = release.get('disambiguation') # Get the "classic" Release type. This data comes from a legacy API # feature before MusicBrainz supported multiple release types. @@ -360,18 +413,17 @@ def album_info(release): if reltype: info.albumtype = reltype.lower() - # Log the new-style "primary" and "secondary" release types. - # Eventually, we'd like to actually store this data, but we just log - # it for now to help understand the differences. + # Set the new-style "primary" and "secondary" release types. + albumtypes = [] if 'primary-type' in release['release-group']: rel_primarytype = release['release-group']['primary-type'] if rel_primarytype: - log.debug('primary MB release type: ' + rel_primarytype.lower()) + albumtypes.append(rel_primarytype.lower()) if 'secondary-type-list' in release['release-group']: if release['release-group']['secondary-type-list']: - log.debug('secondary MB release type(s): ' + ', '.join( - [secondarytype.lower() for secondarytype in - release['release-group']['secondary-type-list']])) + for sec_type in release['release-group']['secondary-type-list']: + albumtypes.append(sec_type.lower()) + info.albumtypes = '; '.join(albumtypes) # Release events. info.country, release_date = _preferred_release_event(release) @@ -402,17 +454,33 @@ def album_info(release): first_medium = release['medium-list'][0] info.media = first_medium.get('format') + if config['musicbrainz']['genres']: + sources = [ + release['release-group'].get('genre-list', []), + release.get('genre-list', []), + ] + genres = Counter() + for source in sources: + for genreitem in source: + genres[genreitem['name']] += int(genreitem['count']) + info.genre = '; '.join(g[0] for g in sorted(genres.items(), + key=lambda g: -g[1])) + + extra_albumdatas = plugins.send('mb_album_extract', data=release) + for extra_albumdata in extra_albumdatas: + info.update(extra_albumdata) + info.decode() return info -def match_album(artist, album, tracks=None): +def match_album(artist, album, tracks=None, extra_tags=None): """Searches for a single album ("release" in MusicBrainz parlance) and returns an iterator over AlbumInfo objects. May raise a MusicBrainzAPIError. The query consists of an artist name, an album name, and, - optionally, a number of tracks on the album. + optionally, a number of tracks on the album and any other extra tags. """ # Build search criteria. criteria = {'release': album.lower().strip()} @@ -422,14 +490,24 @@ def match_album(artist, album, tracks=None): # Various Artists search. criteria['arid'] = VARIOUS_ARTISTS_ID if tracks is not None: - criteria['tracks'] = six.text_type(tracks) + criteria['tracks'] = str(tracks) + + # Additional search cues from existing metadata. + if extra_tags: + for tag in extra_tags: + key = FIELDS_TO_MB_KEYS[tag] + value = str(extra_tags.get(tag, '')).lower().strip() + if key == 'catno': + value = value.replace(' ', '') + if value: + criteria[key] = value # Abort if we have no search terms. if not any(criteria.values()): return try: - log.debug(u'Searching for MusicBrainz releases with: {!r}', criteria) + log.debug('Searching for MusicBrainz releases with: {!r}', criteria) res = musicbrainzngs.search_releases( limit=config['musicbrainz']['searchlimit'].get(int), **criteria) except musicbrainzngs.MusicBrainzError as exc: @@ -470,7 +548,7 @@ def _parse_id(s): no ID can be found, return None. """ # Find the first thing that looks like a UUID/MBID. - match = re.search(u'[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', s) + match = re.search('[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', s) if match: return match.group() @@ -480,19 +558,19 @@ def album_for_id(releaseid): object or None if the album is not found. May raise a MusicBrainzAPIError. """ - log.debug(u'Requesting MusicBrainz release {}', releaseid) + log.debug('Requesting MusicBrainz release {}', releaseid) albumid = _parse_id(releaseid) if not albumid: - log.debug(u'Invalid MBID ({0}).', releaseid) + log.debug('Invalid MBID ({0}).', releaseid) return try: res = musicbrainzngs.get_release_by_id(albumid, RELEASE_INCLUDES) except musicbrainzngs.ResponseError: - log.debug(u'Album ID match failed.') + log.debug('Album ID match failed.') return None except musicbrainzngs.MusicBrainzError as exc: - raise MusicBrainzAPIError(exc, u'get release by ID', albumid, + raise MusicBrainzAPIError(exc, 'get release by ID', albumid, traceback.format_exc()) return album_info(res['release']) @@ -503,14 +581,14 @@ def track_for_id(releaseid): """ trackid = _parse_id(releaseid) if not trackid: - log.debug(u'Invalid MBID ({0}).', releaseid) + log.debug('Invalid MBID ({0}).', releaseid) return try: res = musicbrainzngs.get_recording_by_id(trackid, TRACK_INCLUDES) except musicbrainzngs.ResponseError: - log.debug(u'Track ID match failed.') + log.debug('Track ID match failed.') return None except musicbrainzngs.MusicBrainzError as exc: - raise MusicBrainzAPIError(exc, u'get recording by ID', trackid, + raise MusicBrainzAPIError(exc, 'get recording by ID', trackid, traceback.format_exc()) return track_info(res['recording']) diff --git a/libs/common/beets/config_default.yaml b/libs/common/beets/config_default.yaml index 273f9423..74540891 100644 --- a/libs/common/beets/config_default.yaml +++ b/libs/common/beets/config_default.yaml @@ -7,6 +7,7 @@ import: move: no link: no hardlink: no + reflink: no delete: no resume: ask incremental: no @@ -44,10 +45,20 @@ replace: '^\s+': '' '^-': _ path_sep_replace: _ +drive_sep_replace: _ asciify_paths: false art_filename: cover max_filename_length: 0 +aunique: + keys: albumartist album + disambiguators: albumtype year label catalognum albumdisambig releasegroupdisambig + bracket: '[]' + +overwrite_null: + album: [] + track: [] + plugins: [] pluginpath: [] threaded: yes @@ -91,9 +102,12 @@ statefile: state.pickle musicbrainz: host: musicbrainz.org + https: no ratelimit: 1 ratelimit_interval: 1.0 searchlimit: 5 + extra_tags: [] + genres: no match: strong_rec_thresh: 0.04 @@ -129,6 +143,7 @@ match: ignored: [] required: [] ignored_media: [] + ignore_data_tracks: yes ignore_video_tracks: yes track_length_grace: 10 track_length_max: 30 diff --git a/libs/common/beets/dbcore/__init__.py b/libs/common/beets/dbcore/__init__.py index 689e7202..923c34ca 100644 --- a/libs/common/beets/dbcore/__init__.py +++ b/libs/common/beets/dbcore/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,7 +15,6 @@ """DBCore is an abstract database package that forms the basis for beets' Library. """ -from __future__ import division, absolute_import, print_function from .db import Model, Database from .query import Query, FieldQuery, MatchQuery, AndQuery, OrQuery diff --git a/libs/common/beets/dbcore/db.py b/libs/common/beets/dbcore/db.py index 0f4dc151..acd131be 100644 --- a/libs/common/beets/dbcore/db.py +++ b/libs/common/beets/dbcore/db.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,22 +14,21 @@ """The central Model and Database constructs for DBCore. """ -from __future__ import division, absolute_import, print_function import time import os +import re from collections import defaultdict import threading import sqlite3 import contextlib -import collections import beets -from beets.util.functemplate import Template +from beets.util import functemplate from beets.util import py3_path from beets.dbcore import types from .query import MatchQuery, NullSort, TrueQuery -import six +from collections.abc import Mapping class DBAccessError(Exception): @@ -42,20 +40,30 @@ class DBAccessError(Exception): """ -class FormattedMapping(collections.Mapping): +class FormattedMapping(Mapping): """A `dict`-like formatted view of a model. The accessor `mapping[key]` returns the formatted version of `model[key]` as a unicode string. + The `included_keys` parameter allows filtering the fields that are + returned. By default all fields are returned. Limiting to specific keys can + avoid expensive per-item database queries. + If `for_path` is true, all path separators in the formatted values are replaced. """ - def __init__(self, model, for_path=False): + ALL_KEYS = '*' + + def __init__(self, model, included_keys=ALL_KEYS, for_path=False): self.for_path = for_path self.model = model - self.model_keys = model.keys(True) + if included_keys == self.ALL_KEYS: + # Performance note: this triggers a database query. + self.model_keys = self.model.keys(True) + else: + self.model_keys = included_keys def __getitem__(self, key): if key in self.model_keys: @@ -72,7 +80,7 @@ class FormattedMapping(collections.Mapping): def get(self, key, default=None): if default is None: default = self.model._type(key).format(None) - return super(FormattedMapping, self).get(key, default) + return super().get(key, default) def _get_formatted(self, model, key): value = model._type(key).format(model.get(key)) @@ -81,6 +89,11 @@ class FormattedMapping(collections.Mapping): if self.for_path: sep_repl = beets.config['path_sep_replace'].as_str() + sep_drive = beets.config['drive_sep_replace'].as_str() + + if re.match(r'^\w:', value): + value = re.sub(r'(?<=^\w):', sep_drive, value) + for sep in (os.path.sep, os.path.altsep): if sep: value = value.replace(sep, sep_repl) @@ -88,11 +101,105 @@ class FormattedMapping(collections.Mapping): return value +class LazyConvertDict: + """Lazily convert types for attributes fetched from the database + """ + + def __init__(self, model_cls): + """Initialize the object empty + """ + self.data = {} + self.model_cls = model_cls + self._converted = {} + + def init(self, data): + """Set the base data that should be lazily converted + """ + self.data = data + + def _convert(self, key, value): + """Convert the attribute type according the the SQL type + """ + return self.model_cls._type(key).from_sql(value) + + def __setitem__(self, key, value): + """Set an attribute value, assume it's already converted + """ + self._converted[key] = value + + def __getitem__(self, key): + """Get an attribute value, converting the type on demand + if needed + """ + if key in self._converted: + return self._converted[key] + elif key in self.data: + value = self._convert(key, self.data[key]) + self._converted[key] = value + return value + + def __delitem__(self, key): + """Delete both converted and base data + """ + if key in self._converted: + del self._converted[key] + if key in self.data: + del self.data[key] + + def keys(self): + """Get a list of available field names for this object. + """ + return list(self._converted.keys()) + list(self.data.keys()) + + def copy(self): + """Create a copy of the object. + """ + new = self.__class__(self.model_cls) + new.data = self.data.copy() + new._converted = self._converted.copy() + return new + + # Act like a dictionary. + + def update(self, values): + """Assign all values in the given dict. + """ + for key, value in values.items(): + self[key] = value + + def items(self): + """Iterate over (key, value) pairs that this object contains. + Computed fields are not included. + """ + for key in self: + yield key, self[key] + + def get(self, key, default=None): + """Get the value for a given key or `default` if it does not + exist. + """ + if key in self: + return self[key] + else: + return default + + def __contains__(self, key): + """Determine whether `key` is an attribute on this object. + """ + return key in self.keys() + + def __iter__(self): + """Iterate over the available field names (excluding computed + fields). + """ + return iter(self.keys()) + + # Abstract base for model classes. -class Model(object): +class Model: """An abstract object representing an object in the database. Model - objects act like dictionaries (i.e., the allow subscript access like + objects act like dictionaries (i.e., they allow subscript access like ``obj['field']``). The same field set is available via attribute access as a shortcut (i.e., ``obj.field``). Three kinds of attributes are available: @@ -143,12 +250,22 @@ class Model(object): are subclasses of `Sort`. """ + _queries = {} + """Named queries that use a field-like `name:value` syntax but which + do not relate to any specific field. + """ + _always_dirty = False """By default, fields only become "dirty" when their value actually changes. Enabling this flag marks fields as dirty even when the new value is the same as the old value (e.g., `o.f = o.f`). """ + _revision = -1 + """A revision number from when the model was loaded from or written + to the database. + """ + @classmethod def _getters(cls): """Return a mapping from field names to getter functions. @@ -172,8 +289,8 @@ class Model(object): """ self._db = db self._dirty = set() - self._values_fixed = {} - self._values_flex = {} + self._values_fixed = LazyConvertDict(self) + self._values_flex = LazyConvertDict(self) # Initial contents. self.update(values) @@ -187,23 +304,25 @@ class Model(object): ordinary construction are bypassed. """ obj = cls(db) - for key, value in fixed_values.items(): - obj._values_fixed[key] = cls._type(key).from_sql(value) - for key, value in flex_values.items(): - obj._values_flex[key] = cls._type(key).from_sql(value) + + obj._values_fixed.init(fixed_values) + obj._values_flex.init(flex_values) + return obj def __repr__(self): - return '{0}({1})'.format( + return '{}({})'.format( type(self).__name__, - ', '.join('{0}={1!r}'.format(k, v) for k, v in dict(self).items()), + ', '.join(f'{k}={v!r}' for k, v in dict(self).items()), ) def clear_dirty(self): """Mark all fields as *clean* (i.e., not needing to be stored to - the database). + the database). Also update the revision. """ self._dirty = set() + if self._db: + self._revision = self._db.revision def _check_db(self, need_id=True): """Ensure that this object is associated with a database row: it @@ -212,10 +331,10 @@ class Model(object): """ if not self._db: raise ValueError( - u'{0} has no database'.format(type(self).__name__) + '{} has no database'.format(type(self).__name__) ) if need_id and not self.id: - raise ValueError(u'{0} has no id'.format(type(self).__name__)) + raise ValueError('{} has no id'.format(type(self).__name__)) def copy(self): """Create a copy of the model object. @@ -243,19 +362,32 @@ class Model(object): """ return cls._fields.get(key) or cls._types.get(key) or types.DEFAULT - def __getitem__(self, key): - """Get the value for a field. Raise a KeyError if the field is - not available. + def _get(self, key, default=None, raise_=False): + """Get the value for a field, or `default`. Alternatively, + raise a KeyError if the field is not available. """ getters = self._getters() if key in getters: # Computed. return getters[key](self) elif key in self._fields: # Fixed. - return self._values_fixed.get(key, self._type(key).null) + if key in self._values_fixed: + return self._values_fixed[key] + else: + return self._type(key).null elif key in self._values_flex: # Flexible. return self._values_flex[key] - else: + elif raise_: raise KeyError(key) + else: + return default + + get = _get + + def __getitem__(self, key): + """Get the value for a field. Raise a KeyError if the field is + not available. + """ + return self._get(key, raise_=True) def _setitem(self, key, value): """Assign the value for a field, return whether new and old value @@ -290,12 +422,12 @@ class Model(object): if key in self._values_flex: # Flexible. del self._values_flex[key] self._dirty.add(key) # Mark for dropping on store. + elif key in self._fields: # Fixed + setattr(self, key, self._type(key).null) elif key in self._getters(): # Computed. - raise KeyError(u'computed field {0} cannot be deleted'.format(key)) - elif key in self._fields: # Fixed. - raise KeyError(u'fixed field {0} cannot be deleted'.format(key)) + raise KeyError(f'computed field {key} cannot be deleted') else: - raise KeyError(u'no such field {0}'.format(key)) + raise KeyError(f'no such field {key}') def keys(self, computed=False): """Get a list of available field names for this object. The @@ -330,19 +462,10 @@ class Model(object): for key in self: yield key, self[key] - def get(self, key, default=None): - """Get the value for a given key or `default` if it does not - exist. - """ - if key in self: - return self[key] - else: - return default - def __contains__(self, key): """Determine whether `key` is an attribute on this object. """ - return key in self.keys(True) + return key in self.keys(computed=True) def __iter__(self): """Iterate over the available field names (excluding computed @@ -354,22 +477,22 @@ class Model(object): def __getattr__(self, key): if key.startswith('_'): - raise AttributeError(u'model has no attribute {0!r}'.format(key)) + raise AttributeError(f'model has no attribute {key!r}') else: try: return self[key] except KeyError: - raise AttributeError(u'no such field {0!r}'.format(key)) + raise AttributeError(f'no such field {key!r}') def __setattr__(self, key, value): if key.startswith('_'): - super(Model, self).__setattr__(key, value) + super().__setattr__(key, value) else: self[key] = value def __delattr__(self, key): if key.startswith('_'): - super(Model, self).__delattr__(key) + super().__delattr__(key) else: del self[key] @@ -398,7 +521,7 @@ class Model(object): with self._db.transaction() as tx: # Main table update. if assignments: - query = 'UPDATE {0} SET {1} WHERE id=?'.format( + query = 'UPDATE {} SET {} WHERE id=?'.format( self._table, assignments ) subvars.append(self.id) @@ -409,7 +532,7 @@ class Model(object): if key in self._dirty: self._dirty.remove(key) tx.mutate( - 'INSERT INTO {0} ' + 'INSERT INTO {} ' '(entity_id, key, value) ' 'VALUES (?, ?, ?);'.format(self._flex_table), (self.id, key, value), @@ -418,7 +541,7 @@ class Model(object): # Deleted flexible attributes. for key in self._dirty: tx.mutate( - 'DELETE FROM {0} ' + 'DELETE FROM {} ' 'WHERE entity_id=? AND key=?'.format(self._flex_table), (self.id, key) ) @@ -427,12 +550,18 @@ class Model(object): def load(self): """Refresh the object's metadata from the library database. + + If check_revision is true, the database is only queried loaded when a + transaction has been committed since the item was last loaded. """ self._check_db() + if not self._dirty and self._db.revision == self._revision: + # Exit early + return stored_obj = self._db._get(type(self), self.id) - assert stored_obj is not None, u"object {0} not in DB".format(self.id) - self._values_fixed = {} - self._values_flex = {} + assert stored_obj is not None, f"object {self.id} not in DB" + self._values_fixed = LazyConvertDict(self) + self._values_flex = LazyConvertDict(self) self.update(dict(stored_obj)) self.clear_dirty() @@ -442,11 +571,11 @@ class Model(object): self._check_db() with self._db.transaction() as tx: tx.mutate( - 'DELETE FROM {0} WHERE id=?'.format(self._table), + f'DELETE FROM {self._table} WHERE id=?', (self.id,) ) tx.mutate( - 'DELETE FROM {0} WHERE entity_id=?'.format(self._flex_table), + f'DELETE FROM {self._flex_table} WHERE entity_id=?', (self.id,) ) @@ -464,7 +593,7 @@ class Model(object): with self._db.transaction() as tx: new_id = tx.mutate( - 'INSERT INTO {0} DEFAULT VALUES'.format(self._table) + f'INSERT INTO {self._table} DEFAULT VALUES' ) self.id = new_id self.added = time.time() @@ -479,11 +608,11 @@ class Model(object): _formatter = FormattedMapping - def formatted(self, for_path=False): + def formatted(self, included_keys=_formatter.ALL_KEYS, for_path=False): """Get a mapping containing all values on this object formatted as human-readable unicode strings. """ - return self._formatter(self, for_path) + return self._formatter(self, included_keys, for_path) def evaluate_template(self, template, for_path=False): """Evaluate a template (a string or a `Template` object) using @@ -491,9 +620,9 @@ class Model(object): separators will be added to the template. """ # Perform substitution. - if isinstance(template, six.string_types): - template = Template(template) - return template.substitute(self.formatted(for_path), + if isinstance(template, str): + template = functemplate.template(template) + return template.substitute(self.formatted(for_path=for_path), self._template_funcs()) # Parsing. @@ -502,8 +631,8 @@ class Model(object): def _parse(cls, key, string): """Parse a string as a value for the given key. """ - if not isinstance(string, six.string_types): - raise TypeError(u"_parse() argument must be a string") + if not isinstance(string, str): + raise TypeError("_parse() argument must be a string") return cls._type(key).parse(string) @@ -515,11 +644,13 @@ class Model(object): # Database controller and supporting interfaces. -class Results(object): +class Results: """An item query result set. Iterating over the collection lazily constructs LibModel objects that reflect database rows. """ - def __init__(self, model_class, rows, db, query=None, sort=None): + + def __init__(self, model_class, rows, db, flex_rows, + query=None, sort=None): """Create a result set that will construct objects of type `model_class`. @@ -539,6 +670,7 @@ class Results(object): self.db = db self.query = query self.sort = sort + self.flex_rows = flex_rows # We keep a queue of rows we haven't yet consumed for # materialization. We preserve the original total number of @@ -560,6 +692,10 @@ class Results(object): a `Results` object a second time should be much faster than the first. """ + + # Index flexible attributes by the item ID, so we have easier access + flex_attrs = self._get_indexed_flex_attrs() + index = 0 # Position in the materialized objects. while index < len(self._objects) or self._rows: # Are there previously-materialized objects to produce? @@ -572,7 +708,7 @@ class Results(object): else: while self._rows: row = self._rows.pop(0) - obj = self._make_model(row) + obj = self._make_model(row, flex_attrs.get(row['id'], {})) # If there is a slow-query predicate, ensurer that the # object passes it. if not self.query or self.query.match(obj): @@ -594,20 +730,24 @@ class Results(object): # Objects are pre-sorted (i.e., by the database). return self._get_objects() - def _make_model(self, row): - # Get the flexible attributes for the object. - with self.db.transaction() as tx: - flex_rows = tx.query( - 'SELECT * FROM {0} WHERE entity_id=?'.format( - self.model_class._flex_table - ), - (row['id'],) - ) + def _get_indexed_flex_attrs(self): + """ Index flexible attributes by the entity id they belong to + """ + flex_values = {} + for row in self.flex_rows: + if row['entity_id'] not in flex_values: + flex_values[row['entity_id']] = {} + flex_values[row['entity_id']][row['key']] = row['value'] + + return flex_values + + def _make_model(self, row, flex_values={}): + """ Create a Model object for the given row + """ cols = dict(row) - values = dict((k, v) for (k, v) in cols.items() - if not k[:4] == 'flex') - flex_values = dict((row['key'], row['value']) for row in flex_rows) + values = {k: v for (k, v) in cols.items() + if not k[:4] == 'flex'} # Construct the Python object obj = self.model_class._awaken(self.db, values, flex_values) @@ -656,7 +796,7 @@ class Results(object): next(it) return next(it) except StopIteration: - raise IndexError(u'result index {0} out of range'.format(n)) + raise IndexError(f'result index {n} out of range') def get(self): """Return the first matching object, or None if no objects @@ -669,10 +809,16 @@ class Results(object): return None -class Transaction(object): +class Transaction: """A context manager for safe, concurrent access to the database. All SQL commands should be executed through a transaction. """ + + _mutated = False + """A flag storing whether a mutation has been executed in the + current transaction. + """ + def __init__(self, db): self.db = db @@ -694,12 +840,15 @@ class Transaction(object): entered but not yet exited transaction. If it is the last active transaction, the database updates are committed. """ + # Beware of races; currently secured by db._db_lock + self.db.revision += self._mutated with self.db._tx_stack() as stack: assert stack.pop() is self empty = not stack if empty: # Ending a "root" transaction. End the SQLite transaction. self.db._connection().commit() + self._mutated = False self.db._db_lock.release() def query(self, statement, subvals=()): @@ -715,7 +864,6 @@ class Transaction(object): """ try: cursor = self.db._connection().execute(statement, subvals) - return cursor.lastrowid except sqlite3.OperationalError as e: # In two specific cases, SQLite reports an error while accessing # the underlying database file. We surface these exceptions as @@ -725,26 +873,41 @@ class Transaction(object): raise DBAccessError(e.args[0]) else: raise + else: + self._mutated = True + return cursor.lastrowid def script(self, statements): """Execute a string containing multiple SQL statements.""" + # We don't know whether this mutates, but quite likely it does. + self._mutated = True self.db._connection().executescript(statements) -class Database(object): +class Database: """A container for Model objects that wraps an SQLite database as the backend. """ + _models = () """The Model subclasses representing tables in this database. """ + supports_extensions = hasattr(sqlite3.Connection, 'enable_load_extension') + """Whether or not the current version of SQLite supports extensions""" + + revision = 0 + """The current revision of the database. To be increased whenever + data is written in a transaction. + """ + def __init__(self, path, timeout=5.0): self.path = path self.timeout = timeout self._connections = {} self._tx_stacks = defaultdict(list) + self._extensions = [] # A lock to protect the _connections and _tx_stacks maps, which # both map thread IDs to private resources. @@ -794,6 +957,13 @@ class Database(object): py3_path(self.path), timeout=self.timeout ) + if self.supports_extensions: + conn.enable_load_extension(True) + + # Load any extension that are already loaded for other connections. + for path in self._extensions: + conn.load_extension(path) + # Access SELECT results like dictionaries. conn.row_factory = sqlite3.Row return conn @@ -822,6 +992,18 @@ class Database(object): """ return Transaction(self) + def load_extension(self, path): + """Load an SQLite extension into all open connections.""" + if not self.supports_extensions: + raise ValueError( + 'this sqlite3 installation does not support extensions') + + self._extensions.append(path) + + # Load the extension into every open connection. + for conn in self._connections.values(): + conn.load_extension(path) + # Schema setup and migration. def _make_table(self, table, fields): @@ -831,7 +1013,7 @@ class Database(object): # Get current schema. with self.transaction() as tx: rows = tx.query('PRAGMA table_info(%s)' % table) - current_fields = set([row[1] for row in rows]) + current_fields = {row[1] for row in rows} field_names = set(fields.keys()) if current_fields.issuperset(field_names): @@ -842,9 +1024,9 @@ class Database(object): # No table exists. columns = [] for name, typ in fields.items(): - columns.append('{0} {1}'.format(name, typ.sql)) - setup_sql = 'CREATE TABLE {0} ({1});\n'.format(table, - ', '.join(columns)) + columns.append(f'{name} {typ.sql}') + setup_sql = 'CREATE TABLE {} ({});\n'.format(table, + ', '.join(columns)) else: # Table exists does not match the field set. @@ -852,7 +1034,7 @@ class Database(object): for name, typ in fields.items(): if name in current_fields: continue - setup_sql += 'ALTER TABLE {0} ADD COLUMN {1} {2};\n'.format( + setup_sql += 'ALTER TABLE {} ADD COLUMN {} {};\n'.format( table, name, typ.sql ) @@ -888,17 +1070,31 @@ class Database(object): where, subvals = query.clause() order_by = sort.order_clause() - sql = ("SELECT * FROM {0} WHERE {1} {2}").format( + sql = ("SELECT * FROM {} WHERE {} {}").format( model_cls._table, where or '1', - "ORDER BY {0}".format(order_by) if order_by else '', + f"ORDER BY {order_by}" if order_by else '', + ) + + # Fetch flexible attributes for items matching the main query. + # Doing the per-item filtering in python is faster than issuing + # one query per item to sqlite. + flex_sql = (""" + SELECT * FROM {} WHERE entity_id IN + (SELECT id FROM {} WHERE {}); + """.format( + model_cls._flex_table, + model_cls._table, + where or '1', + ) ) with self.transaction() as tx: rows = tx.query(sql, subvals) + flex_rows = tx.query(flex_sql, subvals) return Results( - model_cls, rows, self, + model_cls, rows, self, flex_rows, None if where else query, # Slow query component. sort if sort.is_slow() else None, # Slow sort component. ) diff --git a/libs/common/beets/dbcore/query.py b/libs/common/beets/dbcore/query.py index 8fb64e20..96476a5b 100644 --- a/libs/common/beets/dbcore/query.py +++ b/libs/common/beets/dbcore/query.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,7 +14,6 @@ """The Query type hierarchy for DBCore. """ -from __future__ import division, absolute_import, print_function import re from operator import mul @@ -23,10 +21,6 @@ from beets import util from datetime import datetime, timedelta import unicodedata from functools import reduce -import six - -if not six.PY2: - buffer = memoryview # sqlite won't accept memoryview in python 2 class ParsingError(ValueError): @@ -44,8 +38,8 @@ class InvalidQueryError(ParsingError): def __init__(self, query, explanation): if isinstance(query, list): query = " ".join(query) - message = u"'{0}': {1}".format(query, explanation) - super(InvalidQueryError, self).__init__(message) + message = f"'{query}': {explanation}" + super().__init__(message) class InvalidQueryArgumentValueError(ParsingError): @@ -56,13 +50,13 @@ class InvalidQueryArgumentValueError(ParsingError): """ def __init__(self, what, expected, detail=None): - message = u"'{0}' is not {1}".format(what, expected) + message = f"'{what}' is not {expected}" if detail: - message = u"{0}: {1}".format(message, detail) - super(InvalidQueryArgumentValueError, self).__init__(message) + message = f"{message}: {detail}" + super().__init__(message) -class Query(object): +class Query: """An abstract class representing a query into the item database. """ @@ -82,7 +76,7 @@ class Query(object): raise NotImplementedError def __repr__(self): - return "{0.__class__.__name__}()".format(self) + return f"{self.__class__.__name__}()" def __eq__(self, other): return type(self) == type(other) @@ -129,7 +123,7 @@ class FieldQuery(Query): "{0.fast})".format(self)) def __eq__(self, other): - return super(FieldQuery, self).__eq__(other) and \ + return super().__eq__(other) and \ self.field == other.field and self.pattern == other.pattern def __hash__(self): @@ -151,17 +145,13 @@ class NoneQuery(FieldQuery): """A query that checks whether a field is null.""" def __init__(self, field, fast=True): - super(NoneQuery, self).__init__(field, None, fast) + super().__init__(field, None, fast) def col_clause(self): return self.field + " IS NULL", () - @classmethod - def match(cls, item): - try: - return item[cls.field] is None - except KeyError: - return True + def match(self, item): + return item.get(self.field) is None def __repr__(self): return "{0.__class__.__name__}({0.field!r}, {0.fast})".format(self) @@ -214,14 +204,14 @@ class RegexpQuery(StringFieldQuery): """ def __init__(self, field, pattern, fast=True): - super(RegexpQuery, self).__init__(field, pattern, fast) + super().__init__(field, pattern, fast) pattern = self._normalize(pattern) try: self.pattern = re.compile(self.pattern) except re.error as exc: # Invalid regular expression. raise InvalidQueryArgumentValueError(pattern, - u"a regular expression", + "a regular expression", format(exc)) @staticmethod @@ -242,8 +232,8 @@ class BooleanQuery(MatchQuery): """ def __init__(self, field, pattern, fast=True): - super(BooleanQuery, self).__init__(field, pattern, fast) - if isinstance(pattern, six.string_types): + super().__init__(field, pattern, fast) + if isinstance(pattern, str): self.pattern = util.str2bool(pattern) self.pattern = int(self.pattern) @@ -256,16 +246,16 @@ class BytesQuery(MatchQuery): """ def __init__(self, field, pattern): - super(BytesQuery, self).__init__(field, pattern) + super().__init__(field, pattern) # Use a buffer/memoryview representation of the pattern for SQLite # matching. This instructs SQLite to treat the blob as binary # rather than encoded Unicode. - if isinstance(self.pattern, (six.text_type, bytes)): - if isinstance(self.pattern, six.text_type): + if isinstance(self.pattern, (str, bytes)): + if isinstance(self.pattern, str): self.pattern = self.pattern.encode('utf-8') - self.buf_pattern = buffer(self.pattern) - elif isinstance(self.pattern, buffer): + self.buf_pattern = memoryview(self.pattern) + elif isinstance(self.pattern, memoryview): self.buf_pattern = self.pattern self.pattern = bytes(self.pattern) @@ -297,10 +287,10 @@ class NumericQuery(FieldQuery): try: return float(s) except ValueError: - raise InvalidQueryArgumentValueError(s, u"an int or a float") + raise InvalidQueryArgumentValueError(s, "an int or a float") def __init__(self, field, pattern, fast=True): - super(NumericQuery, self).__init__(field, pattern, fast) + super().__init__(field, pattern, fast) parts = pattern.split('..', 1) if len(parts) == 1: @@ -318,7 +308,7 @@ class NumericQuery(FieldQuery): if self.field not in item: return False value = item[self.field] - if isinstance(value, six.string_types): + if isinstance(value, str): value = self._convert(value) if self.point is not None: @@ -335,14 +325,14 @@ class NumericQuery(FieldQuery): return self.field + '=?', (self.point,) else: if self.rangemin is not None and self.rangemax is not None: - return (u'{0} >= ? AND {0} <= ?'.format(self.field), + return ('{0} >= ? AND {0} <= ?'.format(self.field), (self.rangemin, self.rangemax)) elif self.rangemin is not None: - return u'{0} >= ?'.format(self.field), (self.rangemin,) + return f'{self.field} >= ?', (self.rangemin,) elif self.rangemax is not None: - return u'{0} <= ?'.format(self.field), (self.rangemax,) + return f'{self.field} <= ?', (self.rangemax,) else: - return u'1', () + return '1', () class CollectionQuery(Query): @@ -387,7 +377,7 @@ class CollectionQuery(Query): return "{0.__class__.__name__}({0.subqueries!r})".format(self) def __eq__(self, other): - return super(CollectionQuery, self).__eq__(other) and \ + return super().__eq__(other) and \ self.subqueries == other.subqueries def __hash__(self): @@ -411,7 +401,7 @@ class AnyFieldQuery(CollectionQuery): subqueries = [] for field in self.fields: subqueries.append(cls(field, pattern, True)) - super(AnyFieldQuery, self).__init__(subqueries) + super().__init__(subqueries) def clause(self): return self.clause_with_joiner('or') @@ -427,7 +417,7 @@ class AnyFieldQuery(CollectionQuery): "{0.query_class.__name__})".format(self)) def __eq__(self, other): - return super(AnyFieldQuery, self).__eq__(other) and \ + return super().__eq__(other) and \ self.query_class == other.query_class def __hash__(self): @@ -453,7 +443,7 @@ class AndQuery(MutableCollectionQuery): return self.clause_with_joiner('and') def match(self, item): - return all([q.match(item) for q in self.subqueries]) + return all(q.match(item) for q in self.subqueries) class OrQuery(MutableCollectionQuery): @@ -463,7 +453,7 @@ class OrQuery(MutableCollectionQuery): return self.clause_with_joiner('or') def match(self, item): - return any([q.match(item) for q in self.subqueries]) + return any(q.match(item) for q in self.subqueries) class NotQuery(Query): @@ -477,7 +467,7 @@ class NotQuery(Query): def clause(self): clause, subvals = self.subquery.clause() if clause: - return 'not ({0})'.format(clause), subvals + return f'not ({clause})', subvals else: # If there is no clause, there is nothing to negate. All the logic # is handled by match() for slow queries. @@ -490,7 +480,7 @@ class NotQuery(Query): return "{0.__class__.__name__}({0.subquery!r})".format(self) def __eq__(self, other): - return super(NotQuery, self).__eq__(other) and \ + return super().__eq__(other) and \ self.subquery == other.subquery def __hash__(self): @@ -546,7 +536,7 @@ def _parse_periods(pattern): return (start, end) -class Period(object): +class Period: """A period of time given by a date, time and precision. Example: 2014-01-01 10:50:30 with precision 'month' represents all @@ -572,7 +562,7 @@ class Period(object): or "second"). """ if precision not in Period.precisions: - raise ValueError(u'Invalid precision {0}'.format(precision)) + raise ValueError(f'Invalid precision {precision}') self.date = date self.precision = precision @@ -653,10 +643,10 @@ class Period(object): elif 'second' == precision: return date + timedelta(seconds=1) else: - raise ValueError(u'unhandled precision {0}'.format(precision)) + raise ValueError(f'unhandled precision {precision}') -class DateInterval(object): +class DateInterval: """A closed-open interval of dates. A left endpoint of None means since the beginning of time. @@ -665,7 +655,7 @@ class DateInterval(object): def __init__(self, start, end): if start is not None and end is not None and not start < end: - raise ValueError(u"start date {0} is not before end date {1}" + raise ValueError("start date {} is not before end date {}" .format(start, end)) self.start = start self.end = end @@ -686,7 +676,7 @@ class DateInterval(object): return True def __str__(self): - return '[{0}, {1})'.format(self.start, self.end) + return f'[{self.start}, {self.end})' class DateQuery(FieldQuery): @@ -700,7 +690,7 @@ class DateQuery(FieldQuery): """ def __init__(self, field, pattern, fast=True): - super(DateQuery, self).__init__(field, pattern, fast) + super().__init__(field, pattern, fast) start, end = _parse_periods(pattern) self.interval = DateInterval.from_periods(start, end) @@ -759,12 +749,12 @@ class DurationQuery(NumericQuery): except ValueError: raise InvalidQueryArgumentValueError( s, - u"a M:SS string or a float") + "a M:SS string or a float") # Sorting. -class Sort(object): +class Sort: """An abstract class representing a sort operation for a query into the item database. """ @@ -851,13 +841,13 @@ class MultipleSort(Sort): return items def __repr__(self): - return 'MultipleSort({!r})'.format(self.sorts) + return f'MultipleSort({self.sorts!r})' def __hash__(self): return hash(tuple(self.sorts)) def __eq__(self, other): - return super(MultipleSort, self).__eq__(other) and \ + return super().__eq__(other) and \ self.sorts == other.sorts @@ -878,14 +868,14 @@ class FieldSort(Sort): def key(item): field_val = item.get(self.field, '') - if self.case_insensitive and isinstance(field_val, six.text_type): + if self.case_insensitive and isinstance(field_val, str): field_val = field_val.lower() return field_val return sorted(objs, key=key, reverse=not self.ascending) def __repr__(self): - return '<{0}: {1}{2}>'.format( + return '<{}: {}{}>'.format( type(self).__name__, self.field, '+' if self.ascending else '-', @@ -895,7 +885,7 @@ class FieldSort(Sort): return hash((self.field, self.ascending)) def __eq__(self, other): - return super(FieldSort, self).__eq__(other) and \ + return super().__eq__(other) and \ self.field == other.field and \ self.ascending == other.ascending @@ -913,7 +903,7 @@ class FixedFieldSort(FieldSort): 'ELSE {0} END)'.format(self.field) else: field = self.field - return "{0} {1}".format(field, order) + return f"{field} {order}" class SlowFieldSort(FieldSort): diff --git a/libs/common/beets/dbcore/queryparse.py b/libs/common/beets/dbcore/queryparse.py index bc9cc77e..3bf02e4d 100644 --- a/libs/common/beets/dbcore/queryparse.py +++ b/libs/common/beets/dbcore/queryparse.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,12 +14,10 @@ """Parsing of strings into DBCore queries. """ -from __future__ import division, absolute_import, print_function import re import itertools from . import query -import beets PARSE_QUERY_PART_REGEX = re.compile( # Non-capturing optional segment for the keyword. @@ -89,7 +86,7 @@ def parse_query_part(part, query_classes={}, prefixes={}, assert match # Regex should always match negate = bool(match.group(1)) key = match.group(2) - term = match.group(3).replace('\:', ':') + term = match.group(3).replace('\\:', ':') # Check whether there's a prefix in the query and use the # corresponding query type. @@ -119,12 +116,13 @@ def construct_query_part(model_cls, prefixes, query_part): if not query_part: return query.TrueQuery() - # Use `model_cls` to build up a map from field names to `Query` - # classes. + # Use `model_cls` to build up a map from field (or query) names to + # `Query` classes. query_classes = {} for k, t in itertools.chain(model_cls._fields.items(), model_cls._types.items()): query_classes[k] = t.query + query_classes.update(model_cls._queries) # Non-field queries. # Parse the string. key, pattern, query_class, negate = \ @@ -137,26 +135,27 @@ def construct_query_part(model_cls, prefixes, query_part): # The query type matches a specific field, but none was # specified. So we use a version of the query that matches # any field. - q = query.AnyFieldQuery(pattern, model_cls._search_fields, - query_class) - if negate: - return query.NotQuery(q) - else: - return q + out_query = query.AnyFieldQuery(pattern, model_cls._search_fields, + query_class) else: # Non-field query type. - if negate: - return query.NotQuery(query_class(pattern)) - else: - return query_class(pattern) + out_query = query_class(pattern) - # Otherwise, this must be a `FieldQuery`. Use the field name to - # construct the query object. - key = key.lower() - q = query_class(key.lower(), pattern, key in model_cls._fields) + # Field queries get constructed according to the name of the field + # they are querying. + elif issubclass(query_class, query.FieldQuery): + key = key.lower() + out_query = query_class(key.lower(), pattern, key in model_cls._fields) + + # Non-field (named) query. + else: + out_query = query_class(pattern) + + # Apply negation. if negate: - return query.NotQuery(q) - return q + return query.NotQuery(out_query) + else: + return out_query def query_from_strings(query_cls, model_cls, prefixes, query_parts): @@ -172,11 +171,13 @@ def query_from_strings(query_cls, model_cls, prefixes, query_parts): return query_cls(subqueries) -def construct_sort_part(model_cls, part): +def construct_sort_part(model_cls, part, case_insensitive=True): """Create a `Sort` from a single string criterion. `model_cls` is the `Model` being queried. `part` is a single string - ending in ``+`` or ``-`` indicating the sort. + ending in ``+`` or ``-`` indicating the sort. `case_insensitive` + indicates whether or not the sort should be performed in a case + sensitive manner. """ assert part, "part must be a field name and + or -" field = part[:-1] @@ -185,7 +186,6 @@ def construct_sort_part(model_cls, part): assert direction in ('+', '-'), "part must end with + or -" is_ascending = direction == '+' - case_insensitive = beets.config['sort_case_insensitive'].get(bool) if field in model_cls._sorts: sort = model_cls._sorts[field](model_cls, is_ascending, case_insensitive) @@ -197,21 +197,23 @@ def construct_sort_part(model_cls, part): return sort -def sort_from_strings(model_cls, sort_parts): +def sort_from_strings(model_cls, sort_parts, case_insensitive=True): """Create a `Sort` from a list of sort criteria (strings). """ if not sort_parts: sort = query.NullSort() elif len(sort_parts) == 1: - sort = construct_sort_part(model_cls, sort_parts[0]) + sort = construct_sort_part(model_cls, sort_parts[0], case_insensitive) else: sort = query.MultipleSort() for part in sort_parts: - sort.add_sort(construct_sort_part(model_cls, part)) + sort.add_sort(construct_sort_part(model_cls, part, + case_insensitive)) return sort -def parse_sorted_query(model_cls, parts, prefixes={}): +def parse_sorted_query(model_cls, parts, prefixes={}, + case_insensitive=True): """Given a list of strings, create the `Query` and `Sort` that they represent. """ @@ -222,8 +224,8 @@ def parse_sorted_query(model_cls, parts, prefixes={}): # Split up query in to comma-separated subqueries, each representing # an AndQuery, which need to be joined together in one OrQuery subquery_parts = [] - for part in parts + [u',']: - if part.endswith(u','): + for part in parts + [',']: + if part.endswith(','): # Ensure we can catch "foo, bar" as well as "foo , bar" last_subquery_part = part[:-1] if last_subquery_part: @@ -237,8 +239,8 @@ def parse_sorted_query(model_cls, parts, prefixes={}): else: # Sort parts (1) end in + or -, (2) don't have a field, and # (3) consist of more than just the + or -. - if part.endswith((u'+', u'-')) \ - and u':' not in part \ + if part.endswith(('+', '-')) \ + and ':' not in part \ and len(part) > 1: sort_parts.append(part) else: @@ -246,5 +248,5 @@ def parse_sorted_query(model_cls, parts, prefixes={}): # Avoid needlessly wrapping single statements in an OR q = query.OrQuery(query_parts) if len(query_parts) > 1 else query_parts[0] - s = sort_from_strings(model_cls, sort_parts) + s = sort_from_strings(model_cls, sort_parts, case_insensitive) return q, s diff --git a/libs/common/beets/dbcore/types.py b/libs/common/beets/dbcore/types.py index b909904b..40f6a080 100644 --- a/libs/common/beets/dbcore/types.py +++ b/libs/common/beets/dbcore/types.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,25 +14,20 @@ """Representation of type information for DBCore model fields. """ -from __future__ import division, absolute_import, print_function from . import query from beets.util import str2bool -import six - -if not six.PY2: - buffer = memoryview # sqlite won't accept memoryview in python 2 # Abstract base. -class Type(object): +class Type: """An object encapsulating the type of a model field. Includes information about how to store, query, format, and parse a given field. """ - sql = u'TEXT' + sql = 'TEXT' """The SQLite column type for the value. """ @@ -41,7 +35,7 @@ class Type(object): """The `Query` subclass to be used when querying the field. """ - model_type = six.text_type + model_type = str """The Python type that is used to represent the value in the model. The model is guaranteed to return a value of this type if the field @@ -63,11 +57,11 @@ class Type(object): value = self.null # `self.null` might be `None` if value is None: - value = u'' + value = '' if isinstance(value, bytes): value = value.decode('utf-8', 'ignore') - return six.text_type(value) + return str(value) def parse(self, string): """Parse a (possibly human-written) string and return the @@ -97,16 +91,16 @@ class Type(object): For fixed fields the type of `value` is determined by the column type affinity given in the `sql` property and the SQL to Python mapping of the database adapter. For more information see: - http://www.sqlite.org/datatype3.html + https://www.sqlite.org/datatype3.html https://docs.python.org/2/library/sqlite3.html#sqlite-and-python-types Flexible fields have the type affinity `TEXT`. This means the - `sql_value` is either a `buffer`/`memoryview` or a `unicode` object` + `sql_value` is either a `memoryview` or a `unicode` object` and the method must handle these in addition. """ - if isinstance(sql_value, buffer): + if isinstance(sql_value, memoryview): sql_value = bytes(sql_value).decode('utf-8', 'ignore') - if isinstance(sql_value, six.text_type): + if isinstance(sql_value, str): return self.parse(sql_value) else: return self.normalize(sql_value) @@ -127,10 +121,18 @@ class Default(Type): class Integer(Type): """A basic integer type. """ - sql = u'INTEGER' + sql = 'INTEGER' query = query.NumericQuery model_type = int + def normalize(self, value): + try: + return self.model_type(round(float(value))) + except ValueError: + return self.null + except TypeError: + return self.null + class PaddedInt(Integer): """An integer field that is formatted with a given number of digits, @@ -140,19 +142,25 @@ class PaddedInt(Integer): self.digits = digits def format(self, value): - return u'{0:0{1}d}'.format(value or 0, self.digits) + return '{0:0{1}d}'.format(value or 0, self.digits) + + +class NullPaddedInt(PaddedInt): + """Same as `PaddedInt`, but does not normalize `None` to `0.0`. + """ + null = None class ScaledInt(Integer): """An integer whose formatting operation scales the number by a constant and adds a suffix. Good for units with large magnitudes. """ - def __init__(self, unit, suffix=u''): + def __init__(self, unit, suffix=''): self.unit = unit self.suffix = suffix def format(self, value): - return u'{0}{1}'.format((value or 0) // self.unit, self.suffix) + return '{}{}'.format((value or 0) // self.unit, self.suffix) class Id(Integer): @@ -163,18 +171,22 @@ class Id(Integer): def __init__(self, primary=True): if primary: - self.sql = u'INTEGER PRIMARY KEY' + self.sql = 'INTEGER PRIMARY KEY' class Float(Type): - """A basic floating-point type. + """A basic floating-point type. The `digits` parameter specifies how + many decimal places to use in the human-readable representation. """ - sql = u'REAL' + sql = 'REAL' query = query.NumericQuery model_type = float + def __init__(self, digits=1): + self.digits = digits + def format(self, value): - return u'{0:.1f}'.format(value or 0.0) + return '{0:.{1}f}'.format(value or 0, self.digits) class NullFloat(Float): @@ -186,19 +198,25 @@ class NullFloat(Float): class String(Type): """A Unicode string type. """ - sql = u'TEXT' + sql = 'TEXT' query = query.SubstringQuery + def normalize(self, value): + if value is None: + return self.null + else: + return self.model_type(value) + class Boolean(Type): """A boolean type. """ - sql = u'INTEGER' + sql = 'INTEGER' query = query.BooleanQuery model_type = bool def format(self, value): - return six.text_type(bool(value)) + return str(bool(value)) def parse(self, string): return str2bool(string) diff --git a/libs/common/beets/importer.py b/libs/common/beets/importer.py index 4e4084ee..561cedd2 100644 --- a/libs/common/beets/importer.py +++ b/libs/common/beets/importer.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function """Provides the basic, interface-agnostic workflow for importing and autotagging music files. @@ -40,7 +38,7 @@ from beets import config from beets.util import pipeline, sorted_walk, ancestry, MoveOperation from beets.util import syspath, normpath, displayable_path from enum import Enum -from beets import mediafile +import mediafile action = Enum('action', ['SKIP', 'ASIS', 'TRACKS', 'APPLY', 'ALBUMS', 'RETAG']) @@ -75,7 +73,7 @@ def _open_state(): # unpickling, including ImportError. We use a catch-all # exception to avoid enumerating them all (the docs don't even have a # full list!). - log.debug(u'state file could not be read: {0}', exc) + log.debug('state file could not be read: {0}', exc) return {} @@ -84,8 +82,8 @@ def _save_state(state): try: with open(config['statefile'].as_filename(), 'wb') as f: pickle.dump(state, f) - except IOError as exc: - log.error(u'state file could not be written: {0}', exc) + except OSError as exc: + log.error('state file could not be written: {0}', exc) # Utilities for reading and writing the beets progress file, which @@ -174,10 +172,11 @@ def history_get(): # Abstract session class. -class ImportSession(object): +class ImportSession: """Controls an import action. Subclasses should implement methods to communicate with the user or otherwise make decisions. """ + def __init__(self, lib, loghandler, paths, query): """Create a session. `lib` is a Library object. `loghandler` is a logging.Handler. Either `paths` or `query` is non-null and indicates @@ -187,7 +186,7 @@ class ImportSession(object): self.logger = self._setup_logging(loghandler) self.paths = paths self.query = query - self._is_resuming = dict() + self._is_resuming = {} self._merged_items = set() self._merged_dirs = set() @@ -222,19 +221,31 @@ class ImportSession(object): iconfig['resume'] = False iconfig['incremental'] = False - # Copy, move, link, and hardlink are mutually exclusive. + if iconfig['reflink']: + iconfig['reflink'] = iconfig['reflink'] \ + .as_choice(['auto', True, False]) + + # Copy, move, reflink, link, and hardlink are mutually exclusive. if iconfig['move']: iconfig['copy'] = False iconfig['link'] = False iconfig['hardlink'] = False + iconfig['reflink'] = False elif iconfig['link']: iconfig['copy'] = False iconfig['move'] = False iconfig['hardlink'] = False + iconfig['reflink'] = False elif iconfig['hardlink']: iconfig['copy'] = False iconfig['move'] = False iconfig['link'] = False + iconfig['reflink'] = False + elif iconfig['reflink']: + iconfig['copy'] = False + iconfig['move'] = False + iconfig['link'] = False + iconfig['hardlink'] = False # Only delete when copying. if not iconfig['copy']: @@ -246,7 +257,7 @@ class ImportSession(object): """Log a message about a given album to the importer log. The status should reflect the reason the album couldn't be tagged. """ - self.logger.info(u'{0} {1}', status, displayable_path(paths)) + self.logger.info('{0} {1}', status, displayable_path(paths)) def log_choice(self, task, duplicate=False): """Logs the task's current choice if it should be logged. If @@ -257,17 +268,17 @@ class ImportSession(object): if duplicate: # Duplicate: log all three choices (skip, keep both, and trump). if task.should_remove_duplicates: - self.tag_log(u'duplicate-replace', paths) + self.tag_log('duplicate-replace', paths) elif task.choice_flag in (action.ASIS, action.APPLY): - self.tag_log(u'duplicate-keep', paths) + self.tag_log('duplicate-keep', paths) elif task.choice_flag is (action.SKIP): - self.tag_log(u'duplicate-skip', paths) + self.tag_log('duplicate-skip', paths) else: # Non-duplicate: log "skip" and "asis" choices. if task.choice_flag is action.ASIS: - self.tag_log(u'asis', paths) + self.tag_log('asis', paths) elif task.choice_flag is action.SKIP: - self.tag_log(u'skip', paths) + self.tag_log('skip', paths) def should_resume(self, path): raise NotImplementedError @@ -284,7 +295,7 @@ class ImportSession(object): def run(self): """Run the import task. """ - self.logger.info(u'import started {0}', time.asctime()) + self.logger.info('import started {0}', time.asctime()) self.set_config(config['import']) # Set up the pipeline. @@ -368,8 +379,8 @@ class ImportSession(object): """Mark paths and directories as merged for future reimport tasks. """ self._merged_items.update(paths) - dirs = set([os.path.dirname(path) if os.path.isfile(path) else path - for path in paths]) + dirs = {os.path.dirname(path) if os.path.isfile(path) else path + for path in paths} self._merged_dirs.update(dirs) def is_resuming(self, toppath): @@ -389,7 +400,7 @@ class ImportSession(object): # Either accept immediately or prompt for input to decide. if self.want_resume is True or \ self.should_resume(toppath): - log.warning(u'Resuming interrupted import of {0}', + log.warning('Resuming interrupted import of {0}', util.displayable_path(toppath)) self._is_resuming[toppath] = True else: @@ -399,11 +410,12 @@ class ImportSession(object): # The importer task class. -class BaseImportTask(object): +class BaseImportTask: """An abstract base class for importer tasks. Tasks flow through the importer pipeline. Each stage can update them. """ + def __init__(self, toppath, paths, items): """Create a task. The primary fields that define a task are: @@ -457,8 +469,9 @@ class ImportTask(BaseImportTask): * `finalize()` Update the import progress and cleanup the file system. """ + def __init__(self, toppath, paths, items): - super(ImportTask, self).__init__(toppath, paths, items) + super().__init__(toppath, paths, items) self.choice_flag = None self.cur_album = None self.cur_artist = None @@ -550,28 +563,34 @@ class ImportTask(BaseImportTask): def remove_duplicates(self, lib): duplicate_items = self.duplicate_items(lib) - log.debug(u'removing {0} old duplicated items', len(duplicate_items)) + log.debug('removing {0} old duplicated items', len(duplicate_items)) for item in duplicate_items: item.remove() if lib.directory in util.ancestry(item.path): - log.debug(u'deleting duplicate {0}', + log.debug('deleting duplicate {0}', util.displayable_path(item.path)) util.remove(item.path) util.prune_dirs(os.path.dirname(item.path), lib.directory) - def set_fields(self): + def set_fields(self, lib): """Sets the fields given at CLI or configuration to the specified - values. + values, for both the album and all its items. """ + items = self.imported_items() for field, view in config['import']['set_fields'].items(): value = view.get() - log.debug(u'Set field {1}={2} for {0}', + log.debug('Set field {1}={2} for {0}', displayable_path(self.paths), field, value) self.album[field] = value - self.album.store() + for item in items: + item[field] = value + with lib.transaction(): + for item in items: + item.store() + self.album.store() def finalize(self, session): """Save progress, clean up files, and emit plugin event. @@ -655,7 +674,7 @@ class ImportTask(BaseImportTask): return [] duplicates = [] - task_paths = set(i.path for i in self.items if i) + task_paths = {i.path for i in self.items if i} duplicate_query = dbcore.AndQuery(( dbcore.MatchQuery('albumartist', artist), dbcore.MatchQuery('album', album), @@ -665,7 +684,7 @@ class ImportTask(BaseImportTask): # Check whether the album paths are all present in the task # i.e. album is being completely re-imported by the task, # in which case it is not a duplicate (will be replaced). - album_paths = set(i.path for i in album.items()) + album_paths = {i.path for i in album.items()} if not (album_paths <= task_paths): duplicates.append(album) return duplicates @@ -707,7 +726,7 @@ class ImportTask(BaseImportTask): item.update(changes) def manipulate_files(self, operation=None, write=False, session=None): - """ Copy, move, link or hardlink (depending on `operation`) the files + """ Copy, move, link, hardlink or reflink (depending on `operation`) the files as well as write metadata. `operation` should be an instance of `util.MoveOperation`. @@ -754,6 +773,8 @@ class ImportTask(BaseImportTask): self.record_replaced(lib) self.remove_replaced(lib) self.album = lib.add_album(self.imported_items()) + if 'data_source' in self.imported_items()[0]: + self.album.data_source = self.imported_items()[0].data_source self.reimport_metadata(lib) def record_replaced(self, lib): @@ -772,7 +793,7 @@ class ImportTask(BaseImportTask): if (not dup_item.album_id or dup_item.album_id in replaced_album_ids): continue - replaced_album = dup_item.get_album() + replaced_album = dup_item._cached_album if replaced_album: replaced_album_ids.add(dup_item.album_id) self.replaced_albums[replaced_album.path] = replaced_album @@ -789,8 +810,8 @@ class ImportTask(BaseImportTask): self.album.artpath = replaced_album.artpath self.album.store() log.debug( - u'Reimported album: added {0}, flexible ' - u'attributes {1} from album {2} for {3}', + 'Reimported album: added {0}, flexible ' + 'attributes {1} from album {2} for {3}', self.album.added, replaced_album._values_flex.keys(), replaced_album.id, @@ -803,16 +824,16 @@ class ImportTask(BaseImportTask): if dup_item.added and dup_item.added != item.added: item.added = dup_item.added log.debug( - u'Reimported item added {0} ' - u'from item {1} for {2}', + 'Reimported item added {0} ' + 'from item {1} for {2}', item.added, dup_item.id, displayable_path(item.path) ) item.update(dup_item._values_flex) log.debug( - u'Reimported item flexible attributes {0} ' - u'from item {1} for {2}', + 'Reimported item flexible attributes {0} ' + 'from item {1} for {2}', dup_item._values_flex.keys(), dup_item.id, displayable_path(item.path) @@ -825,10 +846,10 @@ class ImportTask(BaseImportTask): """ for item in self.imported_items(): for dup_item in self.replaced_items[item]: - log.debug(u'Replacing item {0}: {1}', + log.debug('Replacing item {0}: {1}', dup_item.id, displayable_path(item.path)) dup_item.remove() - log.debug(u'{0} of {1} items replaced', + log.debug('{0} of {1} items replaced', sum(bool(l) for l in self.replaced_items.values()), len(self.imported_items())) @@ -866,7 +887,7 @@ class SingletonImportTask(ImportTask): """ def __init__(self, toppath, item): - super(SingletonImportTask, self).__init__(toppath, [item.path], [item]) + super().__init__(toppath, [item.path], [item]) self.item = item self.is_album = False self.paths = [item.path] @@ -932,13 +953,13 @@ class SingletonImportTask(ImportTask): def reload(self): self.item.load() - def set_fields(self): + def set_fields(self, lib): """Sets the fields given at CLI or configuration to the specified - values. + values, for the singleton item. """ for field, view in config['import']['set_fields'].items(): value = view.get() - log.debug(u'Set field {1}={2} for {0}', + log.debug('Set field {1}={2} for {0}', displayable_path(self.paths), field, value) @@ -959,7 +980,7 @@ class SentinelImportTask(ImportTask): """ def __init__(self, toppath, paths): - super(SentinelImportTask, self).__init__(toppath, paths, ()) + super().__init__(toppath, paths, ()) # TODO Remove the remaining attributes eventually self.should_remove_duplicates = False self.is_album = True @@ -1003,7 +1024,7 @@ class ArchiveImportTask(SentinelImportTask): """ def __init__(self, toppath): - super(ArchiveImportTask, self).__init__(toppath, ()) + super().__init__(toppath, ()) self.extracted = False @classmethod @@ -1032,14 +1053,20 @@ class ArchiveImportTask(SentinelImportTask): cls._handlers = [] from zipfile import is_zipfile, ZipFile cls._handlers.append((is_zipfile, ZipFile)) - from tarfile import is_tarfile, TarFile - cls._handlers.append((is_tarfile, TarFile)) + import tarfile + cls._handlers.append((tarfile.is_tarfile, tarfile.open)) try: from rarfile import is_rarfile, RarFile except ImportError: pass else: cls._handlers.append((is_rarfile, RarFile)) + try: + from py7zr import is_7zfile, SevenZipFile + except ImportError: + pass + else: + cls._handlers.append((is_7zfile, SevenZipFile)) return cls._handlers @@ -1047,7 +1074,7 @@ class ArchiveImportTask(SentinelImportTask): """Removes the temporary directory the archive was extracted to. """ if self.extracted: - log.debug(u'Removing extracted directory: {0}', + log.debug('Removing extracted directory: {0}', displayable_path(self.toppath)) shutil.rmtree(self.toppath) @@ -1059,9 +1086,9 @@ class ArchiveImportTask(SentinelImportTask): if path_test(util.py3_path(self.toppath)): break + extract_to = mkdtemp() + archive = handler_class(util.py3_path(self.toppath), mode='r') try: - extract_to = mkdtemp() - archive = handler_class(util.py3_path(self.toppath), mode='r') archive.extractall(extract_to) finally: archive.close() @@ -1069,10 +1096,11 @@ class ArchiveImportTask(SentinelImportTask): self.toppath = extract_to -class ImportTaskFactory(object): +class ImportTaskFactory: """Generate album and singleton import tasks for all media files indicated by a path. """ + def __init__(self, toppath, session): """Create a new task factory. @@ -1110,14 +1138,12 @@ class ImportTaskFactory(object): if self.session.config['singletons']: for path in paths: tasks = self._create(self.singleton(path)) - for task in tasks: - yield task + yield from tasks yield self.sentinel(dirs) else: tasks = self._create(self.album(paths, dirs)) - for task in tasks: - yield task + yield from tasks # Produce the final sentinel for this toppath to indicate that # it is finished. This is usually just a SentinelImportTask, but @@ -1165,7 +1191,7 @@ class ImportTaskFactory(object): """Return a `SingletonImportTask` for the music file. """ if self.session.already_imported(self.toppath, [path]): - log.debug(u'Skipping previously-imported path: {0}', + log.debug('Skipping previously-imported path: {0}', displayable_path(path)) self.skipped += 1 return None @@ -1186,10 +1212,10 @@ class ImportTaskFactory(object): return None if dirs is None: - dirs = list(set(os.path.dirname(p) for p in paths)) + dirs = list({os.path.dirname(p) for p in paths}) if self.session.already_imported(self.toppath, dirs): - log.debug(u'Skipping previously-imported path: {0}', + log.debug('Skipping previously-imported path: {0}', displayable_path(dirs)) self.skipped += 1 return None @@ -1219,22 +1245,22 @@ class ImportTaskFactory(object): if not (self.session.config['move'] or self.session.config['copy']): - log.warning(u"Archive importing requires either " - u"'copy' or 'move' to be enabled.") + log.warning("Archive importing requires either " + "'copy' or 'move' to be enabled.") return - log.debug(u'Extracting archive: {0}', + log.debug('Extracting archive: {0}', displayable_path(self.toppath)) archive_task = ArchiveImportTask(self.toppath) try: archive_task.extract() except Exception as exc: - log.error(u'extraction failed: {0}', exc) + log.error('extraction failed: {0}', exc) return # Now read albums from the extracted directory. self.toppath = archive_task.toppath - log.debug(u'Archive extracted to: {0}', self.toppath) + log.debug('Archive extracted to: {0}', self.toppath) return archive_task def read_item(self, path): @@ -1250,9 +1276,9 @@ class ImportTaskFactory(object): # Silently ignore non-music files. pass elif isinstance(exc.reason, mediafile.UnreadableFileError): - log.warning(u'unreadable file: {0}', displayable_path(path)) + log.warning('unreadable file: {0}', displayable_path(path)) else: - log.error(u'error reading {0}: {1}', + log.error('error reading {0}: {1}', displayable_path(path), exc) @@ -1291,17 +1317,16 @@ def read_tasks(session): # Generate tasks. task_factory = ImportTaskFactory(toppath, session) - for t in task_factory.tasks(): - yield t + yield from task_factory.tasks() skipped += task_factory.skipped if not task_factory.imported: - log.warning(u'No files imported from {0}', + log.warning('No files imported from {0}', displayable_path(toppath)) # Show skipped directories (due to incremental/resume). if skipped: - log.info(u'Skipped {0} paths.', skipped) + log.info('Skipped {0} paths.', skipped) def query_tasks(session): @@ -1319,7 +1344,7 @@ def query_tasks(session): else: # Search for albums. for album in session.lib.albums(session.query): - log.debug(u'yielding album {0}: {1} - {2}', + log.debug('yielding album {0}: {1} - {2}', album.id, album.albumartist, album.album) items = list(album.items()) _freshen_items(items) @@ -1342,7 +1367,7 @@ def lookup_candidates(session, task): return plugins.send('import_task_start', session=session, task=task) - log.debug(u'Looking up: {0}', displayable_path(task.paths)) + log.debug('Looking up: {0}', displayable_path(task.paths)) # Restrict the initial lookup to IDs specified by the user via the -m # option. Currently all the IDs are passed onto the tasks directly. @@ -1381,8 +1406,7 @@ def user_query(session, task): def emitter(task): for item in task.items: task = SingletonImportTask(task.toppath, item) - for new_task in task.handle_created(session): - yield new_task + yield from task.handle_created(session) yield SentinelImportTask(task.toppath, task.paths) return _extend_pipeline(emitter(task), @@ -1428,30 +1452,30 @@ def resolve_duplicates(session, task): if task.choice_flag in (action.ASIS, action.APPLY, action.RETAG): found_duplicates = task.find_duplicates(session.lib) if found_duplicates: - log.debug(u'found duplicates: {}'.format( + log.debug('found duplicates: {}'.format( [o.id for o in found_duplicates] )) # Get the default action to follow from config. duplicate_action = config['import']['duplicate_action'].as_choice({ - u'skip': u's', - u'keep': u'k', - u'remove': u'r', - u'merge': u'm', - u'ask': u'a', + 'skip': 's', + 'keep': 'k', + 'remove': 'r', + 'merge': 'm', + 'ask': 'a', }) - log.debug(u'default action for duplicates: {0}', duplicate_action) + log.debug('default action for duplicates: {0}', duplicate_action) - if duplicate_action == u's': + if duplicate_action == 's': # Skip new. task.set_choice(action.SKIP) - elif duplicate_action == u'k': + elif duplicate_action == 'k': # Keep both. Do nothing; leave the choice intact. pass - elif duplicate_action == u'r': + elif duplicate_action == 'r': # Remove old. task.should_remove_duplicates = True - elif duplicate_action == u'm': + elif duplicate_action == 'm': # Merge duplicates together task.should_merge_duplicates = True else: @@ -1471,7 +1495,7 @@ def import_asis(session, task): if task.skip: return - log.info(u'{}', displayable_path(task.paths)) + log.info('{}', displayable_path(task.paths)) task.set_choice(action.ASIS) apply_choice(session, task) @@ -1496,7 +1520,7 @@ def apply_choice(session, task): # because then the ``ImportTask`` won't have an `album` for which # it can set the fields. if config['import']['set_fields']: - task.set_fields() + task.set_fields(session.lib) @pipeline.mutator_stage @@ -1534,6 +1558,8 @@ def manipulate_files(session, task): operation = MoveOperation.LINK elif session.config['hardlink']: operation = MoveOperation.HARDLINK + elif session.config['reflink']: + operation = MoveOperation.REFLINK else: operation = None @@ -1552,11 +1578,11 @@ def log_files(session, task): """A coroutine (pipeline stage) to log each file to be imported. """ if isinstance(task, SingletonImportTask): - log.info(u'Singleton: {0}', displayable_path(task.item['path'])) + log.info('Singleton: {0}', displayable_path(task.item['path'])) elif task.items: - log.info(u'Album: {0}', displayable_path(task.paths[0])) + log.info('Album: {0}', displayable_path(task.paths[0])) for item in task.items: - log.info(u' {0}', displayable_path(item['path'])) + log.info(' {0}', displayable_path(item['path'])) def group_albums(session): diff --git a/libs/common/beets/library.py b/libs/common/beets/library.py index ba57407d..888836cd 100644 --- a/libs/common/beets/library.py +++ b/libs/common/beets/library.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,34 +14,30 @@ """The core data store and collection logic for beets. """ -from __future__ import division, absolute_import, print_function import os import sys import unicodedata import time import re -import six +import string +import shlex from beets import logging -from beets.mediafile import MediaFile, UnreadableFileError +from mediafile import MediaFile, UnreadableFileError from beets import plugins from beets import util from beets.util import bytestring_path, syspath, normpath, samefile, \ - MoveOperation -from beets.util.functemplate import Template + MoveOperation, lazy_property +from beets.util.functemplate import template, Template from beets import dbcore from beets.dbcore import types import beets # To use the SQLite "blob" type, it doesn't suffice to provide a byte -# string; SQLite treats that as encoded text. Wrapping it in a `buffer` or a -# `memoryview`, depending on the Python version, tells it that we -# actually mean non-text data. -if six.PY2: - BLOB_TYPE = buffer # noqa: F821 -else: - BLOB_TYPE = memoryview +# string; SQLite treats that as encoded text. Wrapping it in a +# `memoryview` tells it that we actually mean non-text data. +BLOB_TYPE = memoryview log = logging.getLogger('beets') @@ -64,7 +59,7 @@ class PathQuery(dbcore.FieldQuery): `case_sensitive` can be a bool or `None`, indicating that the behavior should depend on the filesystem. """ - super(PathQuery, self).__init__(field, pattern, fast) + super().__init__(field, pattern, fast) # By default, the case sensitivity depends on the filesystem # that the query path is located on. @@ -149,7 +144,7 @@ class PathType(types.Type): `bytes` objects, in keeping with the Unix filesystem abstraction. """ - sql = u'BLOB' + sql = 'BLOB' query = PathQuery model_type = bytes @@ -173,7 +168,7 @@ class PathType(types.Type): return normpath(bytestring_path(string)) def normalize(self, value): - if isinstance(value, six.text_type): + if isinstance(value, str): # Paths stored internally as encoded bytes. return bytestring_path(value) @@ -251,6 +246,7 @@ class SmartArtistSort(dbcore.query.Sort): """Sort by artist (either album artist or track artist), prioritizing the sort field over the raw field. """ + def __init__(self, model_cls, ascending=True, case_insensitive=True): self.album = model_cls is Album self.ascending = ascending @@ -266,12 +262,15 @@ class SmartArtistSort(dbcore.query.Sort): def sort(self, objs): if self.album: - field = lambda a: a.albumartist_sort or a.albumartist + def field(a): + return a.albumartist_sort or a.albumartist else: - field = lambda i: i.artist_sort or i.artist + def field(i): + return i.artist_sort or i.artist if self.case_insensitive: - key = lambda x: field(x).lower() + def key(x): + return field(x).lower() else: key = field return sorted(objs, key=key, reverse=not self.ascending) @@ -282,17 +281,17 @@ PF_KEY_DEFAULT = 'default' # Exceptions. -@six.python_2_unicode_compatible class FileOperationError(Exception): """Indicates an error when interacting with a file on disk. Possibilities include an unsupported media type, a permissions error, and an unhandled Mutagen exception. """ + def __init__(self, path, reason): """Create an exception describing an operation on the file at `path` with the underlying (chained) exception `reason`. """ - super(FileOperationError, self).__init__(path, reason) + super().__init__(path, reason) self.path = path self.reason = reason @@ -300,9 +299,9 @@ class FileOperationError(Exception): """Get a string representing the error. Describes both the underlying reason and the file path in question. """ - return u'{0}: {1}'.format( + return '{}: {}'.format( util.displayable_path(self.path), - six.text_type(self.reason) + str(self.reason) ) # define __str__ as text to avoid infinite loop on super() calls @@ -310,25 +309,24 @@ class FileOperationError(Exception): __str__ = text -@six.python_2_unicode_compatible class ReadError(FileOperationError): """An error while reading a file (i.e. in `Item.read`). """ + def __str__(self): - return u'error reading ' + super(ReadError, self).text() + return 'error reading ' + super().text() -@six.python_2_unicode_compatible class WriteError(FileOperationError): """An error while writing a file (i.e. in `Item.write`). """ + def __str__(self): - return u'error writing ' + super(WriteError, self).text() + return 'error writing ' + super().text() # Item and Album model classes. -@six.python_2_unicode_compatible class LibModel(dbcore.Model): """Shared concrete functionality for Items and Albums. """ @@ -343,21 +341,21 @@ class LibModel(dbcore.Model): return funcs def store(self, fields=None): - super(LibModel, self).store(fields) + super().store(fields) plugins.send('database_change', lib=self._db, model=self) def remove(self): - super(LibModel, self).remove() + super().remove() plugins.send('database_change', lib=self._db, model=self) def add(self, lib=None): - super(LibModel, self).add(lib) + super().add(lib) plugins.send('database_change', lib=self._db, model=self) def __format__(self, spec): if not spec: spec = beets.config[self._format_config_key].as_str() - assert isinstance(spec, six.text_type) + assert isinstance(spec, str) return self.evaluate_template(spec) def __str__(self): @@ -373,15 +371,42 @@ class FormattedItemMapping(dbcore.db.FormattedMapping): Album-level fields take precedence if `for_path` is true. """ - def __init__(self, item, for_path=False): - super(FormattedItemMapping, self).__init__(item, for_path) - self.album = item.get_album() - self.album_keys = [] + ALL_KEYS = '*' + + def __init__(self, item, included_keys=ALL_KEYS, for_path=False): + # We treat album and item keys specially here, + # so exclude transitive album keys from the model's keys. + super().__init__(item, included_keys=[], + for_path=for_path) + self.included_keys = included_keys + if included_keys == self.ALL_KEYS: + # Performance note: this triggers a database query. + self.model_keys = item.keys(computed=True, with_album=False) + else: + self.model_keys = included_keys + self.item = item + + @lazy_property + def all_keys(self): + return set(self.model_keys).union(self.album_keys) + + @lazy_property + def album_keys(self): + album_keys = [] if self.album: - for key in self.album.keys(True): - if key in Album.item_keys or key not in item._fields.keys(): - self.album_keys.append(key) - self.all_keys = set(self.model_keys).union(self.album_keys) + if self.included_keys == self.ALL_KEYS: + # Performance note: this triggers a database query. + for key in self.album.keys(computed=True): + if key in Album.item_keys \ + or key not in self.item._fields.keys(): + album_keys.append(key) + else: + album_keys = self.included_keys + return album_keys + + @property + def album(self): + return self.item._cached_album def _get(self, key): """Get the value for a key, either from the album or the item. @@ -397,19 +422,23 @@ class FormattedItemMapping(dbcore.db.FormattedMapping): raise KeyError(key) def __getitem__(self, key): - """Get the value for a key. Certain unset values are remapped. + """Get the value for a key. `artist` and `albumartist` + are fallback values for each other when not set. """ value = self._get(key) # `artist` and `albumartist` fields fall back to one another. # This is helpful in path formats when the album artist is unset # on as-is imports. - if key == 'artist' and not value: - return self._get('albumartist') - elif key == 'albumartist' and not value: - return self._get('artist') - else: - return value + try: + if key == 'artist' and not value: + return self._get('albumartist') + elif key == 'albumartist' and not value: + return self._get('artist') + except KeyError: + pass + + return value def __iter__(self): return iter(self.all_keys) @@ -422,74 +451,85 @@ class Item(LibModel): _table = 'items' _flex_table = 'item_attributes' _fields = { - 'id': types.PRIMARY_ID, - 'path': PathType(), + 'id': types.PRIMARY_ID, + 'path': PathType(), 'album_id': types.FOREIGN_ID, - 'title': types.STRING, - 'artist': types.STRING, - 'artist_sort': types.STRING, - 'artist_credit': types.STRING, - 'album': types.STRING, - 'albumartist': types.STRING, - 'albumartist_sort': types.STRING, - 'albumartist_credit': types.STRING, - 'genre': types.STRING, - 'lyricist': types.STRING, - 'composer': types.STRING, - 'composer_sort': types.STRING, - 'arranger': types.STRING, - 'grouping': types.STRING, - 'year': types.PaddedInt(4), - 'month': types.PaddedInt(2), - 'day': types.PaddedInt(2), - 'track': types.PaddedInt(2), - 'tracktotal': types.PaddedInt(2), - 'disc': types.PaddedInt(2), - 'disctotal': types.PaddedInt(2), - 'lyrics': types.STRING, - 'comments': types.STRING, - 'bpm': types.INTEGER, - 'comp': types.BOOLEAN, - 'mb_trackid': types.STRING, - 'mb_albumid': types.STRING, - 'mb_artistid': types.STRING, - 'mb_albumartistid': types.STRING, - 'mb_releasetrackid': types.STRING, - 'albumtype': types.STRING, - 'label': types.STRING, + 'title': types.STRING, + 'artist': types.STRING, + 'artist_sort': types.STRING, + 'artist_credit': types.STRING, + 'album': types.STRING, + 'albumartist': types.STRING, + 'albumartist_sort': types.STRING, + 'albumartist_credit': types.STRING, + 'genre': types.STRING, + 'style': types.STRING, + 'discogs_albumid': types.INTEGER, + 'discogs_artistid': types.INTEGER, + 'discogs_labelid': types.INTEGER, + 'lyricist': types.STRING, + 'composer': types.STRING, + 'composer_sort': types.STRING, + 'work': types.STRING, + 'mb_workid': types.STRING, + 'work_disambig': types.STRING, + 'arranger': types.STRING, + 'grouping': types.STRING, + 'year': types.PaddedInt(4), + 'month': types.PaddedInt(2), + 'day': types.PaddedInt(2), + 'track': types.PaddedInt(2), + 'tracktotal': types.PaddedInt(2), + 'disc': types.PaddedInt(2), + 'disctotal': types.PaddedInt(2), + 'lyrics': types.STRING, + 'comments': types.STRING, + 'bpm': types.INTEGER, + 'comp': types.BOOLEAN, + 'mb_trackid': types.STRING, + 'mb_albumid': types.STRING, + 'mb_artistid': types.STRING, + 'mb_albumartistid': types.STRING, + 'mb_releasetrackid': types.STRING, + 'trackdisambig': types.STRING, + 'albumtype': types.STRING, + 'albumtypes': types.STRING, + 'label': types.STRING, 'acoustid_fingerprint': types.STRING, - 'acoustid_id': types.STRING, - 'mb_releasegroupid': types.STRING, - 'asin': types.STRING, - 'catalognum': types.STRING, - 'script': types.STRING, - 'language': types.STRING, - 'country': types.STRING, - 'albumstatus': types.STRING, - 'media': types.STRING, - 'albumdisambig': types.STRING, - 'disctitle': types.STRING, - 'encoder': types.STRING, - 'rg_track_gain': types.NULL_FLOAT, - 'rg_track_peak': types.NULL_FLOAT, - 'rg_album_gain': types.NULL_FLOAT, - 'rg_album_peak': types.NULL_FLOAT, - 'r128_track_gain': types.PaddedInt(6), - 'r128_album_gain': types.PaddedInt(6), - 'original_year': types.PaddedInt(4), - 'original_month': types.PaddedInt(2), - 'original_day': types.PaddedInt(2), - 'initial_key': MusicalKey(), + 'acoustid_id': types.STRING, + 'mb_releasegroupid': types.STRING, + 'asin': types.STRING, + 'isrc': types.STRING, + 'catalognum': types.STRING, + 'script': types.STRING, + 'language': types.STRING, + 'country': types.STRING, + 'albumstatus': types.STRING, + 'media': types.STRING, + 'albumdisambig': types.STRING, + 'releasegroupdisambig': types.STRING, + 'disctitle': types.STRING, + 'encoder': types.STRING, + 'rg_track_gain': types.NULL_FLOAT, + 'rg_track_peak': types.NULL_FLOAT, + 'rg_album_gain': types.NULL_FLOAT, + 'rg_album_peak': types.NULL_FLOAT, + 'r128_track_gain': types.NullPaddedInt(6), + 'r128_album_gain': types.NullPaddedInt(6), + 'original_year': types.PaddedInt(4), + 'original_month': types.PaddedInt(2), + 'original_day': types.PaddedInt(2), + 'initial_key': MusicalKey(), - 'length': DurationType(), - 'bitrate': types.ScaledInt(1000, u'kbps'), - 'format': types.STRING, - 'samplerate': types.ScaledInt(1000, u'kHz'), - 'bitdepth': types.INTEGER, - 'channels': types.INTEGER, - 'mtime': DateType(), - 'added': DateType(), + 'length': DurationType(), + 'bitrate': types.ScaledInt(1000, 'kbps'), + 'format': types.STRING, + 'samplerate': types.ScaledInt(1000, 'kHz'), + 'bitdepth': types.INTEGER, + 'channels': types.INTEGER, + 'mtime': DateType(), + 'added': DateType(), } _search_fields = ('artist', 'title', 'comments', @@ -522,6 +562,29 @@ class Item(LibModel): _format_config_key = 'format_item' + __album = None + """Cached album object. Read-only.""" + + @property + def _cached_album(self): + """The Album object that this item belongs to, if any, or + None if the item is a singleton or is not associated with a + library. + The instance is cached and refreshed on access. + + DO NOT MODIFY! + If you want a copy to modify, use :meth:`get_album`. + """ + if not self.__album and self._db: + self.__album = self._db.get_album(self) + elif self.__album: + self.__album.load() + return self.__album + + @_cached_album.setter + def _cached_album(self, album): + self.__album = album + @classmethod def _getters(cls): getters = plugins.item_field_getters() @@ -544,27 +607,72 @@ class Item(LibModel): """ # Encode unicode paths and read buffers. if key == 'path': - if isinstance(value, six.text_type): + if isinstance(value, str): value = bytestring_path(value) elif isinstance(value, BLOB_TYPE): value = bytes(value) + elif key == 'album_id': + self._cached_album = None - changed = super(Item, self)._setitem(key, value) + changed = super()._setitem(key, value) if changed and key in MediaFile.fields(): self.mtime = 0 # Reset mtime on dirty. + def __getitem__(self, key): + """Get the value for a field, falling back to the album if + necessary. Raise a KeyError if the field is not available. + """ + try: + return super().__getitem__(key) + except KeyError: + if self._cached_album: + return self._cached_album[key] + raise + + def __repr__(self): + # This must not use `with_album=True`, because that might access + # the database. When debugging, that is not guaranteed to succeed, and + # can even deadlock due to the database lock. + return '{}({})'.format( + type(self).__name__, + ', '.join('{}={!r}'.format(k, self[k]) + for k in self.keys(with_album=False)), + ) + + def keys(self, computed=False, with_album=True): + """Get a list of available field names. `with_album` + controls whether the album's fields are included. + """ + keys = super().keys(computed=computed) + if with_album and self._cached_album: + keys = set(keys) + keys.update(self._cached_album.keys(computed=computed)) + keys = list(keys) + return keys + + def get(self, key, default=None, with_album=True): + """Get the value for a given key or `default` if it does not + exist. Set `with_album` to false to skip album fallback. + """ + try: + return self._get(key, default, raise_=with_album) + except KeyError: + if self._cached_album: + return self._cached_album.get(key, default) + return default + def update(self, values): """Set all key/value pairs in the mapping. If mtime is specified, it is not reset (as it might otherwise be). """ - super(Item, self).update(values) + super().update(values) if self.mtime == 0 and 'mtime' in values: self.mtime = values['mtime'] def clear(self): """Set all key/value pairs to None.""" - for key in self._media_fields: + for key in self._media_tag_fields: setattr(self, key, None) def get_album(self): @@ -598,7 +706,7 @@ class Item(LibModel): for key in self._media_fields: value = getattr(mediafile, key) - if isinstance(value, six.integer_types): + if isinstance(value, int): if value.bit_length() > 63: value = 0 self[key] = value @@ -609,7 +717,7 @@ class Item(LibModel): self.path = read_path - def write(self, path=None, tags=None): + def write(self, path=None, tags=None, id3v23=None): """Write the item's metadata to a media file. All fields in `_media_fields` are written to disk according to @@ -621,6 +729,9 @@ class Item(LibModel): `tags` is a dictionary of additional metadata the should be written to the file. (These tags need not be in `_media_fields`.) + `id3v23` will override the global `id3v23` config option if it is + set to something other than `None`. + Can raise either a `ReadError` or a `WriteError`. """ if path is None: @@ -628,6 +739,9 @@ class Item(LibModel): else: path = normpath(path) + if id3v23 is None: + id3v23 = beets.config['id3v23'].get(bool) + # Get the data to write to the file. item_tags = dict(self) item_tags = {k: v for k, v in item_tags.items() @@ -638,8 +752,7 @@ class Item(LibModel): # Open the file. try: - mediafile = MediaFile(syspath(path), - id3v23=beets.config['id3v23'].get(bool)) + mediafile = MediaFile(syspath(path), id3v23=id3v23) except UnreadableFileError as exc: raise ReadError(path, exc) @@ -655,17 +768,17 @@ class Item(LibModel): self.mtime = self.current_mtime() plugins.send('after_write', item=self, path=path) - def try_write(self, path=None, tags=None): + def try_write(self, *args, **kwargs): """Calls `write()` but catches and logs `FileOperationError` exceptions. Returns `False` an exception was caught and `True` otherwise. """ try: - self.write(path, tags) + self.write(*args, **kwargs) return True except FileOperationError as exc: - log.error(u"{0}", exc) + log.error("{0}", exc) return False def try_sync(self, write, move, with_album=True): @@ -685,7 +798,7 @@ class Item(LibModel): if move: # Check whether this file is inside the library directory. if self._db and self._db.directory in util.ancestry(self.path): - log.debug(u'moving {0} to synchronize path', + log.debug('moving {0} to synchronize path', util.displayable_path(self.path)) self.move(with_album=with_album) self.store() @@ -720,6 +833,16 @@ class Item(LibModel): util.hardlink(self.path, dest) plugins.send("item_hardlinked", item=self, source=self.path, destination=dest) + elif operation == MoveOperation.REFLINK: + util.reflink(self.path, dest, fallback=False) + plugins.send("item_reflinked", item=self, source=self.path, + destination=dest) + elif operation == MoveOperation.REFLINK_AUTO: + util.reflink(self.path, dest, fallback=True) + plugins.send("item_reflinked", item=self, source=self.path, + destination=dest) + else: + assert False, 'unknown MoveOperation' # Either copying or moving succeeded, so update the stored path. self.path = dest @@ -738,7 +861,7 @@ class Item(LibModel): try: return os.path.getsize(syspath(self.path)) except (OSError, Exception) as exc: - log.warning(u'could not get filesize: {0}', exc) + log.warning('could not get filesize: {0}', exc) return 0 # Model methods. @@ -748,7 +871,7 @@ class Item(LibModel): removed from disk. If `with_album`, then the item's album (if any) is removed if it the item was the last in the album. """ - super(Item, self).remove() + super().remove() # Remove the album if it is empty. if with_album: @@ -815,7 +938,7 @@ class Item(LibModel): # Templating. def destination(self, fragment=False, basedir=None, platform=None, - path_formats=None): + path_formats=None, replacements=None): """Returns the path in the library directory designated for the item (i.e., where the file ought to be). fragment makes this method return just the path fragment underneath the root library @@ -827,6 +950,8 @@ class Item(LibModel): platform = platform or sys.platform basedir = basedir or self._db.directory path_formats = path_formats or self._db.path_formats + if replacements is None: + replacements = self._db.replacements # Use a path format based on a query, falling back on the # default. @@ -844,11 +969,11 @@ class Item(LibModel): if query == PF_KEY_DEFAULT: break else: - assert False, u"no default path format" + assert False, "no default path format" if isinstance(path_format, Template): subpath_tmpl = path_format else: - subpath_tmpl = Template(path_format) + subpath_tmpl = template(path_format) # Evaluate the selected template. subpath = self.evaluate_template(subpath_tmpl, True) @@ -871,16 +996,16 @@ class Item(LibModel): maxlen = util.max_filename_length(self._db.directory) subpath, fellback = util.legalize_path( - subpath, self._db.replacements, maxlen, + subpath, replacements, maxlen, os.path.splitext(self.path)[1], fragment ) if fellback: # Print an error message if legalization fell back to # default replacements because of the maximum length. log.warning( - u'Fell back to default replacements when naming ' - u'file {}. Configure replacements to avoid lengthening ' - u'the filename.', + 'Fell back to default replacements when naming ' + 'file {}. Configure replacements to avoid lengthening ' + 'the filename.', subpath ) @@ -899,44 +1024,50 @@ class Album(LibModel): _flex_table = 'album_attributes' _always_dirty = True _fields = { - 'id': types.PRIMARY_ID, + 'id': types.PRIMARY_ID, 'artpath': PathType(True), - 'added': DateType(), + 'added': DateType(), - 'albumartist': types.STRING, - 'albumartist_sort': types.STRING, + 'albumartist': types.STRING, + 'albumartist_sort': types.STRING, 'albumartist_credit': types.STRING, - 'album': types.STRING, - 'genre': types.STRING, - 'year': types.PaddedInt(4), - 'month': types.PaddedInt(2), - 'day': types.PaddedInt(2), - 'disctotal': types.PaddedInt(2), - 'comp': types.BOOLEAN, - 'mb_albumid': types.STRING, - 'mb_albumartistid': types.STRING, - 'albumtype': types.STRING, - 'label': types.STRING, - 'mb_releasegroupid': types.STRING, - 'asin': types.STRING, - 'catalognum': types.STRING, - 'script': types.STRING, - 'language': types.STRING, - 'country': types.STRING, - 'albumstatus': types.STRING, - 'albumdisambig': types.STRING, - 'rg_album_gain': types.NULL_FLOAT, - 'rg_album_peak': types.NULL_FLOAT, - 'r128_album_gain': types.PaddedInt(6), - 'original_year': types.PaddedInt(4), - 'original_month': types.PaddedInt(2), - 'original_day': types.PaddedInt(2), + 'album': types.STRING, + 'genre': types.STRING, + 'style': types.STRING, + 'discogs_albumid': types.INTEGER, + 'discogs_artistid': types.INTEGER, + 'discogs_labelid': types.INTEGER, + 'year': types.PaddedInt(4), + 'month': types.PaddedInt(2), + 'day': types.PaddedInt(2), + 'disctotal': types.PaddedInt(2), + 'comp': types.BOOLEAN, + 'mb_albumid': types.STRING, + 'mb_albumartistid': types.STRING, + 'albumtype': types.STRING, + 'albumtypes': types.STRING, + 'label': types.STRING, + 'mb_releasegroupid': types.STRING, + 'asin': types.STRING, + 'catalognum': types.STRING, + 'script': types.STRING, + 'language': types.STRING, + 'country': types.STRING, + 'albumstatus': types.STRING, + 'albumdisambig': types.STRING, + 'releasegroupdisambig': types.STRING, + 'rg_album_gain': types.NULL_FLOAT, + 'rg_album_peak': types.NULL_FLOAT, + 'r128_album_gain': types.NullPaddedInt(6), + 'original_year': types.PaddedInt(4), + 'original_month': types.PaddedInt(2), + 'original_day': types.PaddedInt(2), } _search_fields = ('album', 'albumartist', 'genre') _types = { - 'path': PathType(), + 'path': PathType(), 'data_source': types.STRING, } @@ -952,6 +1083,10 @@ class Album(LibModel): 'albumartist_credit', 'album', 'genre', + 'style', + 'discogs_albumid', + 'discogs_artistid', + 'discogs_labelid', 'year', 'month', 'day', @@ -960,6 +1095,7 @@ class Album(LibModel): 'mb_albumid', 'mb_albumartistid', 'albumtype', + 'albumtypes', 'label', 'mb_releasegroupid', 'asin', @@ -969,6 +1105,7 @@ class Album(LibModel): 'country', 'albumstatus', 'albumdisambig', + 'releasegroupdisambig', 'rg_album_gain', 'rg_album_peak', 'r128_album_gain', @@ -1003,7 +1140,10 @@ class Album(LibModel): containing the album are also removed (recursively) if empty. Set with_items to False to avoid removing the album's items. """ - super(Album, self).remove() + super().remove() + + # Send a 'album_removed' signal to plugins + plugins.send('album_removed', album=self) # Delete art file. if delete: @@ -1027,12 +1167,18 @@ class Album(LibModel): if not old_art: return + if not os.path.exists(old_art): + log.error('removing reference to missing album art file {}', + util.displayable_path(old_art)) + self.artpath = None + return + new_art = self.art_destination(old_art) if new_art == old_art: return new_art = util.unique_path(new_art) - log.debug(u'moving album art {0} to {1}', + log.debug('moving album art {0} to {1}', util.displayable_path(old_art), util.displayable_path(new_art)) if operation == MoveOperation.MOVE: @@ -1044,6 +1190,12 @@ class Album(LibModel): util.link(old_art, new_art) elif operation == MoveOperation.HARDLINK: util.hardlink(old_art, new_art) + elif operation == MoveOperation.REFLINK: + util.reflink(old_art, new_art, fallback=False) + elif operation == MoveOperation.REFLINK_AUTO: + util.reflink(old_art, new_art, fallback=True) + else: + assert False, 'unknown MoveOperation' self.artpath = new_art def move(self, operation=MoveOperation.MOVE, basedir=None, store=True): @@ -1083,7 +1235,7 @@ class Album(LibModel): """ item = self.items().get() if not item: - raise ValueError(u'empty album') + raise ValueError('empty album for album id %d' % self.id) return os.path.dirname(item.path) def _albumtotal(self): @@ -1119,7 +1271,7 @@ class Album(LibModel): image = bytestring_path(image) item_dir = item_dir or self.item_dir() - filename_tmpl = Template( + filename_tmpl = template( beets.config['art_filename'].as_str()) subpath = self.evaluate_template(filename_tmpl, True) if beets.config['asciify_paths']: @@ -1180,7 +1332,7 @@ class Album(LibModel): track_updates[key] = self[key] with self._db.transaction(): - super(Album, self).store(fields) + super().store(fields) if track_updates: for item in self.items(): for key, value in track_updates.items(): @@ -1224,8 +1376,10 @@ def parse_query_parts(parts, model_cls): else: non_path_parts.append(s) + case_insensitive = beets.config['sort_case_insensitive'].get(bool) + query, sort = dbcore.parse_sorted_query( - model_cls, non_path_parts, prefixes + model_cls, non_path_parts, prefixes, case_insensitive ) # Add path queries to aggregate query. @@ -1243,10 +1397,10 @@ def parse_query_string(s, model_cls): The string is split into components using shell-like syntax. """ - message = u"Query is not unicode: {0!r}".format(s) - assert isinstance(s, six.text_type), message + message = f"Query is not unicode: {s!r}" + assert isinstance(s, str), message try: - parts = util.shlex_split(s) + parts = shlex.split(s) except ValueError as exc: raise dbcore.InvalidQueryError(s, exc) return parse_query_parts(parts, model_cls) @@ -1259,10 +1413,7 @@ def _sqlite_bytelower(bytestring): ``-DSQLITE_LIKE_DOESNT_MATCH_BLOBS``. See ``https://github.com/beetbox/beets/issues/2172`` for details. """ - if not six.PY2: - return bytestring.lower() - - return buffer(bytes(bytestring).lower()) # noqa: F821 + return bytestring.lower() # The Library: interface to the database. @@ -1278,7 +1429,7 @@ class Library(dbcore.Database): '$artist/$album/$track $title'),), replacements=None): timeout = beets.config['timeout'].as_number() - super(Library, self).__init__(path, timeout=timeout) + super().__init__(path, timeout=timeout) self.directory = bytestring_path(normpath(directory)) self.path_formats = path_formats @@ -1287,7 +1438,7 @@ class Library(dbcore.Database): self._memotable = {} # Used for template substitution performance. def _create_connection(self): - conn = super(Library, self)._create_connection() + conn = super()._create_connection() conn.create_function('bytelower', 1, _sqlite_bytelower) return conn @@ -1309,10 +1460,10 @@ class Library(dbcore.Database): be empty. """ if not items: - raise ValueError(u'need at least one item') + raise ValueError('need at least one item') # Create the album structure using metadata from the first item. - values = dict((key, items[0][key]) for key in Album.item_keys) + values = {key: items[0][key] for key in Album.item_keys} album = Album(self, **values) # Add the album structure and set the items' album_id fields. @@ -1337,7 +1488,7 @@ class Library(dbcore.Database): # Parse the query, if necessary. try: parsed_sort = None - if isinstance(query, six.string_types): + if isinstance(query, str): query, parsed_sort = parse_query_string(query, model_cls) elif isinstance(query, (list, tuple)): query, parsed_sort = parse_query_parts(query, model_cls) @@ -1349,7 +1500,7 @@ class Library(dbcore.Database): if parsed_sort and not isinstance(parsed_sort, dbcore.query.NullSort): sort = parsed_sort - return super(Library, self)._fetch( + return super()._fetch( model_cls, query, sort ) @@ -1408,7 +1559,7 @@ def _int_arg(s): return int(s.strip()) -class DefaultTemplateFunctions(object): +class DefaultTemplateFunctions: """A container class for the default functions provided to path templates. These functions are contained in an object to provide additional context to the functions -- specifically, the Item being @@ -1447,7 +1598,7 @@ class DefaultTemplateFunctions(object): @staticmethod def tmpl_title(s): """Convert a string to title case.""" - return s.title() + return string.capwords(s) @staticmethod def tmpl_left(s, chars): @@ -1460,7 +1611,7 @@ class DefaultTemplateFunctions(object): return s[-_int_arg(chars):] @staticmethod - def tmpl_if(condition, trueval, falseval=u''): + def tmpl_if(condition, trueval, falseval=''): """If ``condition`` is nonempty and nonzero, emit ``trueval``; otherwise, emit ``falseval`` (if provided). """ @@ -1502,18 +1653,25 @@ class DefaultTemplateFunctions(object): """ # Fast paths: no album, no item or library, or memoized value. if not self.item or not self.lib: - return u'' - if self.item.album_id is None: - return u'' - memokey = ('aunique', keys, disam, self.item.album_id) + return '' + + if isinstance(self.item, Item): + album_id = self.item.album_id + elif isinstance(self.item, Album): + album_id = self.item.id + + if album_id is None: + return '' + + memokey = ('aunique', keys, disam, album_id) memoval = self.lib._memotable.get(memokey) if memoval is not None: return memoval - keys = keys or 'albumartist album' - disam = disam or 'albumtype year label catalognum albumdisambig' + keys = keys or beets.config['aunique']['keys'].as_str() + disam = disam or beets.config['aunique']['disambiguators'].as_str() if bracket is None: - bracket = '[]' + bracket = beets.config['aunique']['bracket'].as_str() keys = keys.split() disam = disam.split() @@ -1522,32 +1680,34 @@ class DefaultTemplateFunctions(object): bracket_l = bracket[0] bracket_r = bracket[1] else: - bracket_l = u'' - bracket_r = u'' + bracket_l = '' + bracket_r = '' - album = self.lib.get_album(self.item) + album = self.lib.get_album(album_id) if not album: # Do nothing for singletons. - self.lib._memotable[memokey] = u'' - return u'' + self.lib._memotable[memokey] = '' + return '' # Find matching albums to disambiguate with. subqueries = [] for key in keys: value = album.get(key, '') - subqueries.append(dbcore.MatchQuery(key, value)) + # Use slow queries for flexible attributes. + fast = key in album.item_keys + subqueries.append(dbcore.MatchQuery(key, value, fast)) albums = self.lib.albums(dbcore.AndQuery(subqueries)) # If there's only one album to matching these details, then do # nothing. if len(albums) == 1: - self.lib._memotable[memokey] = u'' - return u'' + self.lib._memotable[memokey] = '' + return '' # Find the first disambiguator that distinguishes the albums. for disambiguator in disam: # Get the value for each album for the current field. - disam_values = set([a.get(disambiguator, '') for a in albums]) + disam_values = {a.get(disambiguator, '') for a in albums} # If the set of unique values is equal to the number of # albums in the disambiguation set, we're done -- this is @@ -1557,24 +1717,24 @@ class DefaultTemplateFunctions(object): else: # No disambiguator distinguished all fields. - res = u' {1}{0}{2}'.format(album.id, bracket_l, bracket_r) + res = f' {bracket_l}{album.id}{bracket_r}' self.lib._memotable[memokey] = res return res # Flatten disambiguation value into a string. - disam_value = album.formatted(True).get(disambiguator) + disam_value = album.formatted(for_path=True).get(disambiguator) # Return empty string if disambiguator is empty. if disam_value: - res = u' {1}{0}{2}'.format(disam_value, bracket_l, bracket_r) + res = f' {bracket_l}{disam_value}{bracket_r}' else: - res = u'' + res = '' self.lib._memotable[memokey] = res return res @staticmethod - def tmpl_first(s, count=1, skip=0, sep=u'; ', join_str=u'; '): + def tmpl_first(s, count=1, skip=0, sep='; ', join_str='; '): """ Gets the item(s) from x to y in a string separated by something and join then with something @@ -1588,7 +1748,7 @@ class DefaultTemplateFunctions(object): count = skip + int(count) return join_str.join(s.split(sep)[skip:count]) - def tmpl_ifdef(self, field, trueval=u'', falseval=u''): + def tmpl_ifdef(self, field, trueval='', falseval=''): """ If field exists return trueval or the field (default) otherwise, emit return falseval (if provided). @@ -1597,7 +1757,7 @@ class DefaultTemplateFunctions(object): :param falseval: The string if the condition is false :return: The string, based on condition """ - if self.item.formatted().get(field): + if field in self.item: return trueval if trueval else self.item.formatted().get(field) else: return falseval diff --git a/libs/common/beets/logging.py b/libs/common/beets/logging.py index d5ec7b73..4f004f8d 100644 --- a/libs/common/beets/logging.py +++ b/libs/common/beets/logging.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -21,13 +20,11 @@ that when getLogger(name) instantiates a logger that logger uses {}-style formatting. """ -from __future__ import division, absolute_import, print_function from copy import copy from logging import * # noqa import subprocess import threading -import six def logsafe(val): @@ -43,7 +40,7 @@ def logsafe(val): example. """ # Already Unicode. - if isinstance(val, six.text_type): + if isinstance(val, str): return val # Bytestring: needs decoding. @@ -57,7 +54,7 @@ def logsafe(val): # A "problem" object: needs a workaround. elif isinstance(val, subprocess.CalledProcessError): try: - return six.text_type(val) + return str(val) except UnicodeDecodeError: # An object with a broken __unicode__ formatter. Use __str__ # instead. @@ -74,7 +71,7 @@ class StrFormatLogger(Logger): instead of %-style formatting. """ - class _LogMessage(object): + class _LogMessage: def __init__(self, msg, args, kwargs): self.msg = msg self.args = args @@ -82,22 +79,23 @@ class StrFormatLogger(Logger): def __str__(self): args = [logsafe(a) for a in self.args] - kwargs = dict((k, logsafe(v)) for (k, v) in self.kwargs.items()) + kwargs = {k: logsafe(v) for (k, v) in self.kwargs.items()} return self.msg.format(*args, **kwargs) def _log(self, level, msg, args, exc_info=None, extra=None, **kwargs): """Log msg.format(*args, **kwargs)""" m = self._LogMessage(msg, args, kwargs) - return super(StrFormatLogger, self)._log(level, m, (), exc_info, extra) + return super()._log(level, m, (), exc_info, extra) class ThreadLocalLevelLogger(Logger): """A version of `Logger` whose level is thread-local instead of shared. """ + def __init__(self, name, level=NOTSET): self._thread_level = threading.local() self.default_level = NOTSET - super(ThreadLocalLevelLogger, self).__init__(name, level) + super().__init__(name, level) @property def level(self): diff --git a/libs/common/beets/mediafile.py b/libs/common/beets/mediafile.py index 32a32fe1..82bcc973 100644 --- a/libs/common/beets/mediafile.py +++ b/libs/common/beets/mediafile.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -13,2096 +12,15 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""Handles low-level interfacing for files' tags. Wraps Mutagen to -automatically detect file types and provide a unified interface for a -useful subset of music files' tags. -Usage: +import mediafile - >>> f = MediaFile('Lucy.mp3') - >>> f.title - u'Lucy in the Sky with Diamonds' - >>> f.artist = 'The Beatles' - >>> f.save() +import warnings +warnings.warn("beets.mediafile is deprecated; use mediafile instead") -A field will always return a reasonable value of the correct type, even -if no tag is present. If no value is available, the value will be false -(e.g., zero or the empty string). +# Import everything from the mediafile module into this module. +for key, value in mediafile.__dict__.items(): + if key not in ['__name__']: + globals()[key] = value -Internally ``MediaFile`` uses ``MediaField`` descriptors to access the -data from the tags. In turn ``MediaField`` uses a number of -``StorageStyle`` strategies to handle format specific logic. -""" -from __future__ import division, absolute_import, print_function - -import mutagen -import mutagen.id3 -import mutagen.mp4 -import mutagen.flac -import mutagen.asf - -import codecs -import datetime -import re -import base64 -import binascii -import math -import struct -import imghdr -import os -import traceback -import enum -import logging -import six - - -__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile'] - -log = logging.getLogger(__name__) - -# Human-readable type names. -TYPES = { - 'mp3': 'MP3', - 'aac': 'AAC', - 'alac': 'ALAC', - 'ogg': 'OGG', - 'opus': 'Opus', - 'flac': 'FLAC', - 'ape': 'APE', - 'wv': 'WavPack', - 'mpc': 'Musepack', - 'asf': 'Windows Media', - 'aiff': 'AIFF', - 'dsf': 'DSD Stream File', -} - -PREFERRED_IMAGE_EXTENSIONS = {'jpeg': 'jpg'} - - -# Exceptions. - -class UnreadableFileError(Exception): - """Mutagen is not able to extract information from the file. - """ - def __init__(self, path, msg): - Exception.__init__(self, msg if msg else repr(path)) - - -class FileTypeError(UnreadableFileError): - """Reading this type of file is not supported. - - If passed the `mutagen_type` argument this indicates that the - mutagen type is not supported by `Mediafile`. - """ - def __init__(self, path, mutagen_type=None): - if mutagen_type is None: - msg = u'{0!r}: not in a recognized format'.format(path) - else: - msg = u'{0}: of mutagen type {1}'.format(repr(path), mutagen_type) - Exception.__init__(self, msg) - - -class MutagenError(UnreadableFileError): - """Raised when Mutagen fails unexpectedly---probably due to a bug. - """ - def __init__(self, path, mutagen_exc): - msg = u'{0}: {1}'.format(repr(path), mutagen_exc) - Exception.__init__(self, msg) - - -# Interacting with Mutagen. - -def mutagen_call(action, path, func, *args, **kwargs): - """Call a Mutagen function with appropriate error handling. - - `action` is a string describing what the function is trying to do, - and `path` is the relevant filename. The rest of the arguments - describe the callable to invoke. - - We require at least Mutagen 1.33, where `IOError` is *never* used, - neither for internal parsing errors *nor* for ordinary IO error - conditions such as a bad filename. Mutagen-specific parsing errors and IO - errors are reraised as `UnreadableFileError`. Other exceptions - raised inside Mutagen---i.e., bugs---are reraised as `MutagenError`. - """ - try: - return func(*args, **kwargs) - except mutagen.MutagenError as exc: - log.debug(u'%s failed: %s', action, six.text_type(exc)) - raise UnreadableFileError(path, six.text_type(exc)) - except Exception as exc: - # Isolate bugs in Mutagen. - log.debug(u'%s', traceback.format_exc()) - log.error(u'uncaught Mutagen exception in %s: %s', action, exc) - raise MutagenError(path, exc) - - -# Utility. - -def _safe_cast(out_type, val): - """Try to covert val to out_type but never raise an exception. If - the value can't be converted, then a sensible default value is - returned. out_type should be bool, int, or unicode; otherwise, the - value is just passed through. - """ - if val is None: - return None - - if out_type == int: - if isinstance(val, int) or isinstance(val, float): - # Just a number. - return int(val) - else: - # Process any other type as a string. - if isinstance(val, bytes): - val = val.decode('utf-8', 'ignore') - elif not isinstance(val, six.string_types): - val = six.text_type(val) - # Get a number from the front of the string. - match = re.match(r'[\+-]?[0-9]+', val.strip()) - return int(match.group(0)) if match else 0 - - elif out_type == bool: - try: - # Should work for strings, bools, ints: - return bool(int(val)) - except ValueError: - return False - - elif out_type == six.text_type: - if isinstance(val, bytes): - return val.decode('utf-8', 'ignore') - elif isinstance(val, six.text_type): - return val - else: - return six.text_type(val) - - elif out_type == float: - if isinstance(val, int) or isinstance(val, float): - return float(val) - else: - if isinstance(val, bytes): - val = val.decode('utf-8', 'ignore') - else: - val = six.text_type(val) - match = re.match(r'[\+-]?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)', - val.strip()) - if match: - val = match.group(0) - if val: - return float(val) - return 0.0 - - else: - return val - - -# Image coding for ASF/WMA. - -def _unpack_asf_image(data): - """Unpack image data from a WM/Picture tag. Return a tuple - containing the MIME type, the raw image data, a type indicator, and - the image's description. - - This function is treated as "untrusted" and could throw all manner - of exceptions (out-of-bounds, etc.). We should clean this up - sometime so that the failure modes are well-defined. - """ - type, size = struct.unpack_from(' 0: - gain = math.log10(maxgain / 1000.0) * -10 - else: - # Invalid gain value found. - gain = 0.0 - - # SoundCheck stores peak values as the actual value of the sample, - # and again separately for the left and right channels. We need to - # convert this to a percentage of full scale, which is 32768 for a - # 16 bit sample. Once again, we play it safe by using the larger of - # the two values. - peak = max(soundcheck[6:8]) / 32768.0 - - return round(gain, 2), round(peak, 6) - - -def _sc_encode(gain, peak): - """Encode ReplayGain gain/peak values as a Sound Check string. - """ - # SoundCheck stores the peak value as the actual value of the - # sample, rather than the percentage of full scale that RG uses, so - # we do a simple conversion assuming 16 bit samples. - peak *= 32768.0 - - # SoundCheck stores absolute RMS values in some unknown units rather - # than the dB values RG uses. We can calculate these absolute values - # from the gain ratio using a reference value of 1000 units. We also - # enforce the maximum value here, which is equivalent to about - # -18.2dB. - g1 = int(min(round((10 ** (gain / -10)) * 1000), 65534)) - # Same as above, except our reference level is 2500 units. - g2 = int(min(round((10 ** (gain / -10)) * 2500), 65534)) - - # The purpose of these values are unknown, but they also seem to be - # unused so we just use zero. - uk = 0 - values = (g1, g1, g2, g2, uk, uk, int(peak), int(peak), uk, uk) - return (u' %08X' * 10) % values - - -# Cover art and other images. -def _imghdr_what_wrapper(data): - """A wrapper around imghdr.what to account for jpeg files that can only be - identified as such using their magic bytes - See #1545 - See https://github.com/file/file/blob/master/magic/Magdir/jpeg#L12 - """ - # imghdr.what returns none for jpegs with only the magic bytes, so - # _wider_test_jpeg is run in that case. It still returns None if it didn't - # match such a jpeg file. - return imghdr.what(None, h=data) or _wider_test_jpeg(data) - - -def _wider_test_jpeg(data): - """Test for a jpeg file following the UNIX file implementation which - uses the magic bytes rather than just looking for the bytes that - represent 'JFIF' or 'EXIF' at a fixed position. - """ - if data[:2] == b'\xff\xd8': - return 'jpeg' - - -def image_mime_type(data): - """Return the MIME type of the image data (a bytestring). - """ - # This checks for a jpeg file with only the magic bytes (unrecognized by - # imghdr.what). imghdr.what returns none for that type of file, so - # _wider_test_jpeg is run in that case. It still returns None if it didn't - # match such a jpeg file. - kind = _imghdr_what_wrapper(data) - if kind in ['gif', 'jpeg', 'png', 'tiff', 'bmp']: - return 'image/{0}'.format(kind) - elif kind == 'pgm': - return 'image/x-portable-graymap' - elif kind == 'pbm': - return 'image/x-portable-bitmap' - elif kind == 'ppm': - return 'image/x-portable-pixmap' - elif kind == 'xbm': - return 'image/x-xbitmap' - else: - return 'image/x-{0}'.format(kind) - - -def image_extension(data): - ext = _imghdr_what_wrapper(data) - return PREFERRED_IMAGE_EXTENSIONS.get(ext, ext) - - -class ImageType(enum.Enum): - """Indicates the kind of an `Image` stored in a file's tag. - """ - other = 0 - icon = 1 - other_icon = 2 - front = 3 - back = 4 - leaflet = 5 - media = 6 - lead_artist = 7 - artist = 8 - conductor = 9 - group = 10 - composer = 11 - lyricist = 12 - recording_location = 13 - recording_session = 14 - performance = 15 - screen_capture = 16 - fish = 17 - illustration = 18 - artist_logo = 19 - publisher_logo = 20 - - -class Image(object): - """Structure representing image data and metadata that can be - stored and retrieved from tags. - - The structure has four properties. - * ``data`` The binary data of the image - * ``desc`` An optional description of the image - * ``type`` An instance of `ImageType` indicating the kind of image - * ``mime_type`` Read-only property that contains the mime type of - the binary data - """ - def __init__(self, data, desc=None, type=None): - assert isinstance(data, bytes) - if desc is not None: - assert isinstance(desc, six.text_type) - self.data = data - self.desc = desc - if isinstance(type, int): - try: - type = list(ImageType)[type] - except IndexError: - log.debug(u"ignoring unknown image type index %s", type) - type = ImageType.other - self.type = type - - @property - def mime_type(self): - if self.data: - return image_mime_type(self.data) - - @property - def type_index(self): - if self.type is None: - # This method is used when a tag format requires the type - # index to be set, so we return "other" as the default value. - return 0 - return self.type.value - - -# StorageStyle classes describe strategies for accessing values in -# Mutagen file objects. - -class StorageStyle(object): - """A strategy for storing a value for a certain tag format (or set - of tag formats). This basic StorageStyle describes simple 1:1 - mapping from raw values to keys in a Mutagen file object; subclasses - describe more sophisticated translations or format-specific access - strategies. - - MediaFile uses a StorageStyle via three methods: ``get()``, - ``set()``, and ``delete()``. It passes a Mutagen file object to - each. - - Internally, the StorageStyle implements ``get()`` and ``set()`` - using two steps that may be overridden by subtypes. To get a value, - the StorageStyle first calls ``fetch()`` to retrieve the value - corresponding to a key and then ``deserialize()`` to convert the raw - Mutagen value to a consumable Python value. Similarly, to set a - field, we call ``serialize()`` to encode the value and then - ``store()`` to assign the result into the Mutagen object. - - Each StorageStyle type has a class-level `formats` attribute that is - a list of strings indicating the formats that the style applies to. - MediaFile only uses StorageStyles that apply to the correct type for - a given audio file. - """ - - formats = ['FLAC', 'OggOpus', 'OggTheora', 'OggSpeex', 'OggVorbis', - 'OggFlac', 'APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio'] - """List of mutagen classes the StorageStyle can handle. - """ - - def __init__(self, key, as_type=six.text_type, suffix=None, - float_places=2): - """Create a basic storage strategy. Parameters: - - - `key`: The key on the Mutagen file object used to access the - field's data. - - `as_type`: The Python type that the value is stored as - internally (`unicode`, `int`, `bool`, or `bytes`). - - `suffix`: When `as_type` is a string type, append this before - storing the value. - - `float_places`: When the value is a floating-point number and - encoded as a string, the number of digits to store after the - decimal point. - """ - self.key = key - self.as_type = as_type - self.suffix = suffix - self.float_places = float_places - - # Convert suffix to correct string type. - if self.suffix and self.as_type is six.text_type \ - and not isinstance(self.suffix, six.text_type): - self.suffix = self.suffix.decode('utf-8') - - # Getter. - - def get(self, mutagen_file): - """Get the value for the field using this style. - """ - return self.deserialize(self.fetch(mutagen_file)) - - def fetch(self, mutagen_file): - """Retrieve the raw value of for this tag from the Mutagen file - object. - """ - try: - return mutagen_file[self.key][0] - except (KeyError, IndexError): - return None - - def deserialize(self, mutagen_value): - """Given a raw value stored on a Mutagen object, decode and - return the represented value. - """ - if self.suffix and isinstance(mutagen_value, six.text_type) \ - and mutagen_value.endswith(self.suffix): - return mutagen_value[:-len(self.suffix)] - else: - return mutagen_value - - # Setter. - - def set(self, mutagen_file, value): - """Assign the value for the field using this style. - """ - self.store(mutagen_file, self.serialize(value)) - - def store(self, mutagen_file, value): - """Store a serialized value in the Mutagen file object. - """ - mutagen_file[self.key] = [value] - - def serialize(self, value): - """Convert the external Python value to a type that is suitable for - storing in a Mutagen file object. - """ - if isinstance(value, float) and self.as_type is six.text_type: - value = u'{0:.{1}f}'.format(value, self.float_places) - value = self.as_type(value) - elif self.as_type is six.text_type: - if isinstance(value, bool): - # Store bools as 1/0 instead of True/False. - value = six.text_type(int(bool(value))) - elif isinstance(value, bytes): - value = value.decode('utf-8', 'ignore') - else: - value = six.text_type(value) - else: - value = self.as_type(value) - - if self.suffix: - value += self.suffix - - return value - - def delete(self, mutagen_file): - """Remove the tag from the file. - """ - if self.key in mutagen_file: - del mutagen_file[self.key] - - -class ListStorageStyle(StorageStyle): - """Abstract storage style that provides access to lists. - - The ListMediaField descriptor uses a ListStorageStyle via two - methods: ``get_list()`` and ``set_list()``. It passes a Mutagen file - object to each. - - Subclasses may overwrite ``fetch`` and ``store``. ``fetch`` must - return a (possibly empty) list and ``store`` receives a serialized - list of values as the second argument. - - The `serialize` and `deserialize` methods (from the base - `StorageStyle`) are still called with individual values. This class - handles packing and unpacking the values into lists. - """ - def get(self, mutagen_file): - """Get the first value in the field's value list. - """ - try: - return self.get_list(mutagen_file)[0] - except IndexError: - return None - - def get_list(self, mutagen_file): - """Get a list of all values for the field using this style. - """ - return [self.deserialize(item) for item in self.fetch(mutagen_file)] - - def fetch(self, mutagen_file): - """Get the list of raw (serialized) values. - """ - try: - return mutagen_file[self.key] - except KeyError: - return [] - - def set(self, mutagen_file, value): - """Set an individual value as the only value for the field using - this style. - """ - self.set_list(mutagen_file, [value]) - - def set_list(self, mutagen_file, values): - """Set all values for the field using this style. `values` - should be an iterable. - """ - self.store(mutagen_file, [self.serialize(value) for value in values]) - - def store(self, mutagen_file, values): - """Set the list of all raw (serialized) values for this field. - """ - mutagen_file[self.key] = values - - -class SoundCheckStorageStyleMixin(object): - """A mixin for storage styles that read and write iTunes SoundCheck - analysis values. The object must have an `index` field that - indicates which half of the gain/peak pair---0 or 1---the field - represents. - """ - def get(self, mutagen_file): - data = self.fetch(mutagen_file) - if data is not None: - return _sc_decode(data)[self.index] - - def set(self, mutagen_file, value): - data = self.fetch(mutagen_file) - if data is None: - gain_peak = [0, 0] - else: - gain_peak = list(_sc_decode(data)) - gain_peak[self.index] = value or 0 - data = self.serialize(_sc_encode(*gain_peak)) - self.store(mutagen_file, data) - - -class ASFStorageStyle(ListStorageStyle): - """A general storage style for Windows Media/ASF files. - """ - formats = ['ASF'] - - def deserialize(self, data): - if isinstance(data, mutagen.asf.ASFBaseAttribute): - data = data.value - return data - - -class MP4StorageStyle(StorageStyle): - """A general storage style for MPEG-4 tags. - """ - formats = ['MP4'] - - def serialize(self, value): - value = super(MP4StorageStyle, self).serialize(value) - if self.key.startswith('----:') and isinstance(value, six.text_type): - value = value.encode('utf-8') - return value - - -class MP4TupleStorageStyle(MP4StorageStyle): - """A style for storing values as part of a pair of numbers in an - MPEG-4 file. - """ - def __init__(self, key, index=0, **kwargs): - super(MP4TupleStorageStyle, self).__init__(key, **kwargs) - self.index = index - - def deserialize(self, mutagen_value): - items = mutagen_value or [] - packing_length = 2 - return list(items) + [0] * (packing_length - len(items)) - - def get(self, mutagen_file): - value = super(MP4TupleStorageStyle, self).get(mutagen_file)[self.index] - if value == 0: - # The values are always present and saved as integers. So we - # assume that "0" indicates it is not set. - return None - else: - return value - - def set(self, mutagen_file, value): - if value is None: - value = 0 - items = self.deserialize(self.fetch(mutagen_file)) - items[self.index] = int(value) - self.store(mutagen_file, items) - - def delete(self, mutagen_file): - if self.index == 0: - super(MP4TupleStorageStyle, self).delete(mutagen_file) - else: - self.set(mutagen_file, None) - - -class MP4ListStorageStyle(ListStorageStyle, MP4StorageStyle): - pass - - -class MP4SoundCheckStorageStyle(SoundCheckStorageStyleMixin, MP4StorageStyle): - def __init__(self, key, index=0, **kwargs): - super(MP4SoundCheckStorageStyle, self).__init__(key, **kwargs) - self.index = index - - -class MP4BoolStorageStyle(MP4StorageStyle): - """A style for booleans in MPEG-4 files. (MPEG-4 has an atom type - specifically for representing booleans.) - """ - def get(self, mutagen_file): - try: - return mutagen_file[self.key] - except KeyError: - return None - - def get_list(self, mutagen_file): - raise NotImplementedError(u'MP4 bool storage does not support lists') - - def set(self, mutagen_file, value): - mutagen_file[self.key] = value - - def set_list(self, mutagen_file, values): - raise NotImplementedError(u'MP4 bool storage does not support lists') - - -class MP4ImageStorageStyle(MP4ListStorageStyle): - """Store images as MPEG-4 image atoms. Values are `Image` objects. - """ - def __init__(self, **kwargs): - super(MP4ImageStorageStyle, self).__init__(key='covr', **kwargs) - - def deserialize(self, data): - return Image(data) - - def serialize(self, image): - if image.mime_type == 'image/png': - kind = mutagen.mp4.MP4Cover.FORMAT_PNG - elif image.mime_type == 'image/jpeg': - kind = mutagen.mp4.MP4Cover.FORMAT_JPEG - else: - raise ValueError(u'MP4 files only supports PNG and JPEG images') - return mutagen.mp4.MP4Cover(image.data, kind) - - -class MP3StorageStyle(StorageStyle): - """Store data in ID3 frames. - """ - formats = ['MP3', 'AIFF', 'DSF'] - - def __init__(self, key, id3_lang=None, **kwargs): - """Create a new ID3 storage style. `id3_lang` is the value for - the language field of newly created frames. - """ - self.id3_lang = id3_lang - super(MP3StorageStyle, self).__init__(key, **kwargs) - - def fetch(self, mutagen_file): - try: - return mutagen_file[self.key].text[0] - except (KeyError, IndexError): - return None - - def store(self, mutagen_file, value): - frame = mutagen.id3.Frames[self.key](encoding=3, text=[value]) - mutagen_file.tags.setall(self.key, [frame]) - - -class MP3PeopleStorageStyle(MP3StorageStyle): - """Store list of people in ID3 frames. - """ - def __init__(self, key, involvement='', **kwargs): - self.involvement = involvement - super(MP3PeopleStorageStyle, self).__init__(key, **kwargs) - - def store(self, mutagen_file, value): - frames = mutagen_file.tags.getall(self.key) - - # Try modifying in place. - found = False - for frame in frames: - if frame.encoding == mutagen.id3.Encoding.UTF8: - for pair in frame.people: - if pair[0].lower() == self.involvement.lower(): - pair[1] = value - found = True - - # Try creating a new frame. - if not found: - frame = mutagen.id3.Frames[self.key]( - encoding=mutagen.id3.Encoding.UTF8, - people=[[self.involvement, value]] - ) - mutagen_file.tags.add(frame) - - def fetch(self, mutagen_file): - for frame in mutagen_file.tags.getall(self.key): - for pair in frame.people: - if pair[0].lower() == self.involvement.lower(): - try: - return pair[1] - except IndexError: - return None - - -class MP3ListStorageStyle(ListStorageStyle, MP3StorageStyle): - """Store lists of data in multiple ID3 frames. - """ - def fetch(self, mutagen_file): - try: - return mutagen_file[self.key].text - except KeyError: - return [] - - def store(self, mutagen_file, values): - frame = mutagen.id3.Frames[self.key](encoding=3, text=values) - mutagen_file.tags.setall(self.key, [frame]) - - -class MP3UFIDStorageStyle(MP3StorageStyle): - """Store string data in a UFID ID3 frame with a particular owner. - """ - def __init__(self, owner, **kwargs): - self.owner = owner - super(MP3UFIDStorageStyle, self).__init__('UFID:' + owner, **kwargs) - - def fetch(self, mutagen_file): - try: - return mutagen_file[self.key].data - except KeyError: - return None - - def store(self, mutagen_file, value): - # This field type stores text data as encoded data. - assert isinstance(value, six.text_type) - value = value.encode('utf-8') - - frames = mutagen_file.tags.getall(self.key) - for frame in frames: - # Replace existing frame data. - if frame.owner == self.owner: - frame.data = value - else: - # New frame. - frame = mutagen.id3.UFID(owner=self.owner, data=value) - mutagen_file.tags.setall(self.key, [frame]) - - -class MP3DescStorageStyle(MP3StorageStyle): - """Store data in a TXXX (or similar) ID3 frame. The frame is - selected based its ``desc`` field. - """ - def __init__(self, desc=u'', key='TXXX', **kwargs): - assert isinstance(desc, six.text_type) - self.description = desc - super(MP3DescStorageStyle, self).__init__(key=key, **kwargs) - - def store(self, mutagen_file, value): - frames = mutagen_file.tags.getall(self.key) - if self.key != 'USLT': - value = [value] - - # Try modifying in place. - found = False - for frame in frames: - if frame.desc.lower() == self.description.lower(): - frame.text = value - frame.encoding = mutagen.id3.Encoding.UTF8 - found = True - - # Try creating a new frame. - if not found: - frame = mutagen.id3.Frames[self.key]( - desc=self.description, - text=value, - encoding=mutagen.id3.Encoding.UTF8, - ) - if self.id3_lang: - frame.lang = self.id3_lang - mutagen_file.tags.add(frame) - - def fetch(self, mutagen_file): - for frame in mutagen_file.tags.getall(self.key): - if frame.desc.lower() == self.description.lower(): - if self.key == 'USLT': - return frame.text - try: - return frame.text[0] - except IndexError: - return None - - def delete(self, mutagen_file): - found_frame = None - for frame in mutagen_file.tags.getall(self.key): - if frame.desc.lower() == self.description.lower(): - found_frame = frame - break - if found_frame is not None: - del mutagen_file[frame.HashKey] - - -class MP3SlashPackStorageStyle(MP3StorageStyle): - """Store value as part of pair that is serialized as a slash- - separated string. - """ - def __init__(self, key, pack_pos=0, **kwargs): - super(MP3SlashPackStorageStyle, self).__init__(key, **kwargs) - self.pack_pos = pack_pos - - def _fetch_unpacked(self, mutagen_file): - data = self.fetch(mutagen_file) - if data: - items = six.text_type(data).split('/') - else: - items = [] - packing_length = 2 - return list(items) + [None] * (packing_length - len(items)) - - def get(self, mutagen_file): - return self._fetch_unpacked(mutagen_file)[self.pack_pos] - - def set(self, mutagen_file, value): - items = self._fetch_unpacked(mutagen_file) - items[self.pack_pos] = value - if items[0] is None: - items[0] = '' - if items[1] is None: - items.pop() # Do not store last value - self.store(mutagen_file, '/'.join(map(six.text_type, items))) - - def delete(self, mutagen_file): - if self.pack_pos == 0: - super(MP3SlashPackStorageStyle, self).delete(mutagen_file) - else: - self.set(mutagen_file, None) - - -class MP3ImageStorageStyle(ListStorageStyle, MP3StorageStyle): - """Converts between APIC frames and ``Image`` instances. - - The `get_list` method inherited from ``ListStorageStyle`` returns a - list of ``Image``s. Similarly, the `set_list` method accepts a - list of ``Image``s as its ``values`` argument. - """ - def __init__(self): - super(MP3ImageStorageStyle, self).__init__(key='APIC') - self.as_type = bytes - - def deserialize(self, apic_frame): - """Convert APIC frame into Image.""" - return Image(data=apic_frame.data, desc=apic_frame.desc, - type=apic_frame.type) - - def fetch(self, mutagen_file): - return mutagen_file.tags.getall(self.key) - - def store(self, mutagen_file, frames): - mutagen_file.tags.setall(self.key, frames) - - def delete(self, mutagen_file): - mutagen_file.tags.delall(self.key) - - def serialize(self, image): - """Return an APIC frame populated with data from ``image``. - """ - assert isinstance(image, Image) - frame = mutagen.id3.Frames[self.key]() - frame.data = image.data - frame.mime = image.mime_type - frame.desc = image.desc or u'' - - # For compatibility with OS X/iTunes prefer latin-1 if possible. - # See issue #899 - try: - frame.desc.encode("latin-1") - except UnicodeEncodeError: - frame.encoding = mutagen.id3.Encoding.UTF16 - else: - frame.encoding = mutagen.id3.Encoding.LATIN1 - - frame.type = image.type_index - return frame - - -class MP3SoundCheckStorageStyle(SoundCheckStorageStyleMixin, - MP3DescStorageStyle): - def __init__(self, index=0, **kwargs): - super(MP3SoundCheckStorageStyle, self).__init__(**kwargs) - self.index = index - - -class ASFImageStorageStyle(ListStorageStyle): - """Store images packed into Windows Media/ASF byte array attributes. - Values are `Image` objects. - """ - formats = ['ASF'] - - def __init__(self): - super(ASFImageStorageStyle, self).__init__(key='WM/Picture') - - def deserialize(self, asf_picture): - mime, data, type, desc = _unpack_asf_image(asf_picture.value) - return Image(data, desc=desc, type=type) - - def serialize(self, image): - pic = mutagen.asf.ASFByteArrayAttribute() - pic.value = _pack_asf_image(image.mime_type, image.data, - type=image.type_index, - description=image.desc or u'') - return pic - - -class VorbisImageStorageStyle(ListStorageStyle): - """Store images in Vorbis comments. Both legacy COVERART fields and - modern METADATA_BLOCK_PICTURE tags are supported. Data is - base64-encoded. Values are `Image` objects. - """ - formats = ['OggOpus', 'OggTheora', 'OggSpeex', 'OggVorbis', - 'OggFlac'] - - def __init__(self): - super(VorbisImageStorageStyle, self).__init__( - key='metadata_block_picture' - ) - self.as_type = bytes - - def fetch(self, mutagen_file): - images = [] - if 'metadata_block_picture' not in mutagen_file: - # Try legacy COVERART tags. - if 'coverart' in mutagen_file: - for data in mutagen_file['coverart']: - images.append(Image(base64.b64decode(data))) - return images - for data in mutagen_file["metadata_block_picture"]: - try: - pic = mutagen.flac.Picture(base64.b64decode(data)) - except (TypeError, AttributeError): - continue - images.append(Image(data=pic.data, desc=pic.desc, - type=pic.type)) - return images - - def store(self, mutagen_file, image_data): - # Strip all art, including legacy COVERART. - if 'coverart' in mutagen_file: - del mutagen_file['coverart'] - if 'coverartmime' in mutagen_file: - del mutagen_file['coverartmime'] - super(VorbisImageStorageStyle, self).store(mutagen_file, image_data) - - def serialize(self, image): - """Turn a Image into a base64 encoded FLAC picture block. - """ - pic = mutagen.flac.Picture() - pic.data = image.data - pic.type = image.type_index - pic.mime = image.mime_type - pic.desc = image.desc or u'' - - # Encoding with base64 returns bytes on both Python 2 and 3. - # Mutagen requires the data to be a Unicode string, so we decode - # it before passing it along. - return base64.b64encode(pic.write()).decode('ascii') - - -class FlacImageStorageStyle(ListStorageStyle): - """Converts between ``mutagen.flac.Picture`` and ``Image`` instances. - """ - formats = ['FLAC'] - - def __init__(self): - super(FlacImageStorageStyle, self).__init__(key='') - - def fetch(self, mutagen_file): - return mutagen_file.pictures - - def deserialize(self, flac_picture): - return Image(data=flac_picture.data, desc=flac_picture.desc, - type=flac_picture.type) - - def store(self, mutagen_file, pictures): - """``pictures`` is a list of mutagen.flac.Picture instances. - """ - mutagen_file.clear_pictures() - for pic in pictures: - mutagen_file.add_picture(pic) - - def serialize(self, image): - """Turn a Image into a mutagen.flac.Picture. - """ - pic = mutagen.flac.Picture() - pic.data = image.data - pic.type = image.type_index - pic.mime = image.mime_type - pic.desc = image.desc or u'' - return pic - - def delete(self, mutagen_file): - """Remove all images from the file. - """ - mutagen_file.clear_pictures() - - -class APEv2ImageStorageStyle(ListStorageStyle): - """Store images in APEv2 tags. Values are `Image` objects. - """ - formats = ['APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio', 'OptimFROG'] - - TAG_NAMES = { - ImageType.other: 'Cover Art (other)', - ImageType.icon: 'Cover Art (icon)', - ImageType.other_icon: 'Cover Art (other icon)', - ImageType.front: 'Cover Art (front)', - ImageType.back: 'Cover Art (back)', - ImageType.leaflet: 'Cover Art (leaflet)', - ImageType.media: 'Cover Art (media)', - ImageType.lead_artist: 'Cover Art (lead)', - ImageType.artist: 'Cover Art (artist)', - ImageType.conductor: 'Cover Art (conductor)', - ImageType.group: 'Cover Art (band)', - ImageType.composer: 'Cover Art (composer)', - ImageType.lyricist: 'Cover Art (lyricist)', - ImageType.recording_location: 'Cover Art (studio)', - ImageType.recording_session: 'Cover Art (recording)', - ImageType.performance: 'Cover Art (performance)', - ImageType.screen_capture: 'Cover Art (movie scene)', - ImageType.fish: 'Cover Art (colored fish)', - ImageType.illustration: 'Cover Art (illustration)', - ImageType.artist_logo: 'Cover Art (band logo)', - ImageType.publisher_logo: 'Cover Art (publisher logo)', - } - - def __init__(self): - super(APEv2ImageStorageStyle, self).__init__(key='') - - def fetch(self, mutagen_file): - images = [] - for cover_type, cover_tag in self.TAG_NAMES.items(): - try: - frame = mutagen_file[cover_tag] - text_delimiter_index = frame.value.find(b'\x00') - if text_delimiter_index > 0: - comment = frame.value[0:text_delimiter_index] - comment = comment.decode('utf-8', 'replace') - else: - comment = None - image_data = frame.value[text_delimiter_index + 1:] - images.append(Image(data=image_data, type=cover_type, - desc=comment)) - except KeyError: - pass - - return images - - def set_list(self, mutagen_file, values): - self.delete(mutagen_file) - - for image in values: - image_type = image.type or ImageType.other - comment = image.desc or '' - image_data = comment.encode('utf-8') + b'\x00' + image.data - cover_tag = self.TAG_NAMES[image_type] - mutagen_file[cover_tag] = image_data - - def delete(self, mutagen_file): - """Remove all images from the file. - """ - for cover_tag in self.TAG_NAMES.values(): - try: - del mutagen_file[cover_tag] - except KeyError: - pass - - -# MediaField is a descriptor that represents a single logical field. It -# aggregates several StorageStyles describing how to access the data for -# each file type. - -class MediaField(object): - """A descriptor providing access to a particular (abstract) metadata - field. - """ - def __init__(self, *styles, **kwargs): - """Creates a new MediaField. - - :param styles: `StorageStyle` instances that describe the strategy - for reading and writing the field in particular - formats. There must be at least one style for - each possible file format. - - :param out_type: the type of the value that should be returned when - getting this property. - - """ - self.out_type = kwargs.get('out_type', six.text_type) - self._styles = styles - - def styles(self, mutagen_file): - """Yields the list of storage styles of this field that can - handle the MediaFile's format. - """ - for style in self._styles: - if mutagen_file.__class__.__name__ in style.formats: - yield style - - def __get__(self, mediafile, owner=None): - out = None - for style in self.styles(mediafile.mgfile): - out = style.get(mediafile.mgfile) - if out: - break - return _safe_cast(self.out_type, out) - - def __set__(self, mediafile, value): - if value is None: - value = self._none_value() - for style in self.styles(mediafile.mgfile): - style.set(mediafile.mgfile, value) - - def __delete__(self, mediafile): - for style in self.styles(mediafile.mgfile): - style.delete(mediafile.mgfile) - - def _none_value(self): - """Get an appropriate "null" value for this field's type. This - is used internally when setting the field to None. - """ - if self.out_type == int: - return 0 - elif self.out_type == float: - return 0.0 - elif self.out_type == bool: - return False - elif self.out_type == six.text_type: - return u'' - - -class ListMediaField(MediaField): - """Property descriptor that retrieves a list of multiple values from - a tag. - - Uses ``get_list`` and set_list`` methods of its ``StorageStyle`` - strategies to do the actual work. - """ - def __get__(self, mediafile, _): - values = [] - for style in self.styles(mediafile.mgfile): - values.extend(style.get_list(mediafile.mgfile)) - return [_safe_cast(self.out_type, value) for value in values] - - def __set__(self, mediafile, values): - for style in self.styles(mediafile.mgfile): - style.set_list(mediafile.mgfile, values) - - def single_field(self): - """Returns a ``MediaField`` descriptor that gets and sets the - first item. - """ - options = {'out_type': self.out_type} - return MediaField(*self._styles, **options) - - -class DateField(MediaField): - """Descriptor that handles serializing and deserializing dates - - The getter parses value from tags into a ``datetime.date`` instance - and setter serializes such an instance into a string. - - For granular access to year, month, and day, use the ``*_field`` - methods to create corresponding `DateItemField`s. - """ - def __init__(self, *date_styles, **kwargs): - """``date_styles`` is a list of ``StorageStyle``s to store and - retrieve the whole date from. The ``year`` option is an - additional list of fallback styles for the year. The year is - always set on this style, but is only retrieved if the main - storage styles do not return a value. - """ - super(DateField, self).__init__(*date_styles) - year_style = kwargs.get('year', None) - if year_style: - self._year_field = MediaField(*year_style) - - def __get__(self, mediafile, owner=None): - year, month, day = self._get_date_tuple(mediafile) - if not year: - return None - try: - return datetime.date( - year, - month or 1, - day or 1 - ) - except ValueError: # Out of range values. - return None - - def __set__(self, mediafile, date): - if date is None: - self._set_date_tuple(mediafile, None, None, None) - else: - self._set_date_tuple(mediafile, date.year, date.month, date.day) - - def __delete__(self, mediafile): - super(DateField, self).__delete__(mediafile) - if hasattr(self, '_year_field'): - self._year_field.__delete__(mediafile) - - def _get_date_tuple(self, mediafile): - """Get a 3-item sequence representing the date consisting of a - year, month, and day number. Each number is either an integer or - None. - """ - # Get the underlying data and split on hyphens and slashes. - datestring = super(DateField, self).__get__(mediafile, None) - if isinstance(datestring, six.string_types): - datestring = re.sub(r'[Tt ].*$', '', six.text_type(datestring)) - items = re.split('[-/]', six.text_type(datestring)) - else: - items = [] - - # Ensure that we have exactly 3 components, possibly by - # truncating or padding. - items = items[:3] - if len(items) < 3: - items += [None] * (3 - len(items)) - - # Use year field if year is missing. - if not items[0] and hasattr(self, '_year_field'): - items[0] = self._year_field.__get__(mediafile) - - # Convert each component to an integer if possible. - items_ = [] - for item in items: - try: - items_.append(int(item)) - except (TypeError, ValueError): - items_.append(None) - return items_ - - def _set_date_tuple(self, mediafile, year, month=None, day=None): - """Set the value of the field given a year, month, and day - number. Each number can be an integer or None to indicate an - unset component. - """ - if year is None: - self.__delete__(mediafile) - return - - date = [u'{0:04d}'.format(int(year))] - if month: - date.append(u'{0:02d}'.format(int(month))) - if month and day: - date.append(u'{0:02d}'.format(int(day))) - date = map(six.text_type, date) - super(DateField, self).__set__(mediafile, u'-'.join(date)) - - if hasattr(self, '_year_field'): - self._year_field.__set__(mediafile, year) - - def year_field(self): - return DateItemField(self, 0) - - def month_field(self): - return DateItemField(self, 1) - - def day_field(self): - return DateItemField(self, 2) - - -class DateItemField(MediaField): - """Descriptor that gets and sets constituent parts of a `DateField`: - the month, day, or year. - """ - def __init__(self, date_field, item_pos): - self.date_field = date_field - self.item_pos = item_pos - - def __get__(self, mediafile, _): - return self.date_field._get_date_tuple(mediafile)[self.item_pos] - - def __set__(self, mediafile, value): - items = self.date_field._get_date_tuple(mediafile) - items[self.item_pos] = value - self.date_field._set_date_tuple(mediafile, *items) - - def __delete__(self, mediafile): - self.__set__(mediafile, None) - - -class CoverArtField(MediaField): - """A descriptor that provides access to the *raw image data* for the - cover image on a file. This is used for backwards compatibility: the - full `ImageListField` provides richer `Image` objects. - - When there are multiple images we try to pick the most likely to be a front - cover. - """ - def __init__(self): - pass - - def __get__(self, mediafile, _): - candidates = mediafile.images - if candidates: - return self.guess_cover_image(candidates).data - else: - return None - - @staticmethod - def guess_cover_image(candidates): - if len(candidates) == 1: - return candidates[0] - try: - return next(c for c in candidates if c.type == ImageType.front) - except StopIteration: - return candidates[0] - - def __set__(self, mediafile, data): - if data: - mediafile.images = [Image(data=data)] - else: - mediafile.images = [] - - def __delete__(self, mediafile): - delattr(mediafile, 'images') - - -class ImageListField(ListMediaField): - """Descriptor to access the list of images embedded in tags. - - The getter returns a list of `Image` instances obtained from - the tags. The setter accepts a list of `Image` instances to be - written to the tags. - """ - def __init__(self): - # The storage styles used here must implement the - # `ListStorageStyle` interface and get and set lists of - # `Image`s. - super(ImageListField, self).__init__( - MP3ImageStorageStyle(), - MP4ImageStorageStyle(), - ASFImageStorageStyle(), - VorbisImageStorageStyle(), - FlacImageStorageStyle(), - APEv2ImageStorageStyle(), - out_type=Image, - ) - - -# MediaFile is a collection of fields. - -class MediaFile(object): - """Represents a multimedia file on disk and provides access to its - metadata. - """ - def __init__(self, path, id3v23=False): - """Constructs a new `MediaFile` reflecting the file at path. May - throw `UnreadableFileError`. - - By default, MP3 files are saved with ID3v2.4 tags. You can use - the older ID3v2.3 standard by specifying the `id3v23` option. - """ - self.path = path - - self.mgfile = mutagen_call('open', path, mutagen.File, path) - - if self.mgfile is None: - # Mutagen couldn't guess the type - raise FileTypeError(path) - elif (type(self.mgfile).__name__ == 'M4A' or - type(self.mgfile).__name__ == 'MP4'): - info = self.mgfile.info - if info.codec and info.codec.startswith('alac'): - self.type = 'alac' - else: - self.type = 'aac' - elif (type(self.mgfile).__name__ == 'ID3' or - type(self.mgfile).__name__ == 'MP3'): - self.type = 'mp3' - elif type(self.mgfile).__name__ == 'FLAC': - self.type = 'flac' - elif type(self.mgfile).__name__ == 'OggOpus': - self.type = 'opus' - elif type(self.mgfile).__name__ == 'OggVorbis': - self.type = 'ogg' - elif type(self.mgfile).__name__ == 'MonkeysAudio': - self.type = 'ape' - elif type(self.mgfile).__name__ == 'WavPack': - self.type = 'wv' - elif type(self.mgfile).__name__ == 'Musepack': - self.type = 'mpc' - elif type(self.mgfile).__name__ == 'ASF': - self.type = 'asf' - elif type(self.mgfile).__name__ == 'AIFF': - self.type = 'aiff' - elif type(self.mgfile).__name__ == 'DSF': - self.type = 'dsf' - else: - raise FileTypeError(path, type(self.mgfile).__name__) - - # Add a set of tags if it's missing. - if self.mgfile.tags is None: - self.mgfile.add_tags() - - # Set the ID3v2.3 flag only for MP3s. - self.id3v23 = id3v23 and self.type == 'mp3' - - def save(self): - """Write the object's tags back to the file. May - throw `UnreadableFileError`. - """ - # Possibly save the tags to ID3v2.3. - kwargs = {} - if self.id3v23: - id3 = self.mgfile - if hasattr(id3, 'tags'): - # In case this is an MP3 object, not an ID3 object. - id3 = id3.tags - id3.update_to_v23() - kwargs['v2_version'] = 3 - - mutagen_call('save', self.path, self.mgfile.save, **kwargs) - - def delete(self): - """Remove the current metadata tag from the file. May - throw `UnreadableFileError`. - """ - mutagen_call('delete', self.path, self.mgfile.delete) - - # Convenient access to the set of available fields. - - @classmethod - def fields(cls): - """Get the names of all writable properties that reflect - metadata tags (i.e., those that are instances of - :class:`MediaField`). - """ - for property, descriptor in cls.__dict__.items(): - if isinstance(descriptor, MediaField): - if isinstance(property, bytes): - # On Python 2, class field names are bytes. This method - # produces text strings. - yield property.decode('utf8', 'ignore') - else: - yield property - - @classmethod - def _field_sort_name(cls, name): - """Get a sort key for a field name that determines the order - fields should be written in. - - Fields names are kept unchanged, unless they are instances of - :class:`DateItemField`, in which case `year`, `month`, and `day` - are replaced by `date0`, `date1`, and `date2`, respectively, to - make them appear in that order. - """ - if isinstance(cls.__dict__[name], DateItemField): - name = re.sub('year', 'date0', name) - name = re.sub('month', 'date1', name) - name = re.sub('day', 'date2', name) - return name - - @classmethod - def sorted_fields(cls): - """Get the names of all writable metadata fields, sorted in the - order that they should be written. - - This is a lexicographic order, except for instances of - :class:`DateItemField`, which are sorted in year-month-day - order. - """ - for property in sorted(cls.fields(), key=cls._field_sort_name): - yield property - - @classmethod - def readable_fields(cls): - """Get all metadata fields: the writable ones from - :meth:`fields` and also other audio properties. - """ - for property in cls.fields(): - yield property - for property in ('length', 'samplerate', 'bitdepth', 'bitrate', - 'channels', 'format'): - yield property - - @classmethod - def add_field(cls, name, descriptor): - """Add a field to store custom tags. - - :param name: the name of the property the field is accessed - through. It must not already exist on this class. - - :param descriptor: an instance of :class:`MediaField`. - """ - if not isinstance(descriptor, MediaField): - raise ValueError( - u'{0} must be an instance of MediaField'.format(descriptor)) - if name in cls.__dict__: - raise ValueError( - u'property "{0}" already exists on MediaField'.format(name)) - setattr(cls, name, descriptor) - - def update(self, dict): - """Set all field values from a dictionary. - - For any key in `dict` that is also a field to store tags the - method retrieves the corresponding value from `dict` and updates - the `MediaFile`. If a key has the value `None`, the - corresponding property is deleted from the `MediaFile`. - """ - for field in self.sorted_fields(): - if field in dict: - if dict[field] is None: - delattr(self, field) - else: - setattr(self, field, dict[field]) - - # Field definitions. - - title = MediaField( - MP3StorageStyle('TIT2'), - MP4StorageStyle('\xa9nam'), - StorageStyle('TITLE'), - ASFStorageStyle('Title'), - ) - artist = MediaField( - MP3StorageStyle('TPE1'), - MP4StorageStyle('\xa9ART'), - StorageStyle('ARTIST'), - ASFStorageStyle('Author'), - ) - album = MediaField( - MP3StorageStyle('TALB'), - MP4StorageStyle('\xa9alb'), - StorageStyle('ALBUM'), - ASFStorageStyle('WM/AlbumTitle'), - ) - genres = ListMediaField( - MP3ListStorageStyle('TCON'), - MP4ListStorageStyle('\xa9gen'), - ListStorageStyle('GENRE'), - ASFStorageStyle('WM/Genre'), - ) - genre = genres.single_field() - - lyricist = MediaField( - MP3StorageStyle('TEXT'), - MP4StorageStyle('----:com.apple.iTunes:LYRICIST'), - StorageStyle('LYRICIST'), - ASFStorageStyle('WM/Writer'), - ) - composer = MediaField( - MP3StorageStyle('TCOM'), - MP4StorageStyle('\xa9wrt'), - StorageStyle('COMPOSER'), - ASFStorageStyle('WM/Composer'), - ) - composer_sort = MediaField( - MP3StorageStyle('TSOC'), - MP4StorageStyle('soco'), - StorageStyle('COMPOSERSORT'), - ASFStorageStyle('WM/Composersortorder'), - ) - arranger = MediaField( - MP3PeopleStorageStyle('TIPL', involvement='arranger'), - MP4StorageStyle('----:com.apple.iTunes:Arranger'), - StorageStyle('ARRANGER'), - ASFStorageStyle('beets/Arranger'), - ) - - grouping = MediaField( - MP3StorageStyle('TIT1'), - MP4StorageStyle('\xa9grp'), - StorageStyle('GROUPING'), - ASFStorageStyle('WM/ContentGroupDescription'), - ) - track = MediaField( - MP3SlashPackStorageStyle('TRCK', pack_pos=0), - MP4TupleStorageStyle('trkn', index=0), - StorageStyle('TRACK'), - StorageStyle('TRACKNUMBER'), - ASFStorageStyle('WM/TrackNumber'), - out_type=int, - ) - tracktotal = MediaField( - MP3SlashPackStorageStyle('TRCK', pack_pos=1), - MP4TupleStorageStyle('trkn', index=1), - StorageStyle('TRACKTOTAL'), - StorageStyle('TRACKC'), - StorageStyle('TOTALTRACKS'), - ASFStorageStyle('TotalTracks'), - out_type=int, - ) - disc = MediaField( - MP3SlashPackStorageStyle('TPOS', pack_pos=0), - MP4TupleStorageStyle('disk', index=0), - StorageStyle('DISC'), - StorageStyle('DISCNUMBER'), - ASFStorageStyle('WM/PartOfSet'), - out_type=int, - ) - disctotal = MediaField( - MP3SlashPackStorageStyle('TPOS', pack_pos=1), - MP4TupleStorageStyle('disk', index=1), - StorageStyle('DISCTOTAL'), - StorageStyle('DISCC'), - StorageStyle('TOTALDISCS'), - ASFStorageStyle('TotalDiscs'), - out_type=int, - ) - lyrics = MediaField( - MP3DescStorageStyle(key='USLT'), - MP4StorageStyle('\xa9lyr'), - StorageStyle('LYRICS'), - ASFStorageStyle('WM/Lyrics'), - ) - comments = MediaField( - MP3DescStorageStyle(key='COMM'), - MP4StorageStyle('\xa9cmt'), - StorageStyle('DESCRIPTION'), - StorageStyle('COMMENT'), - ASFStorageStyle('WM/Comments'), - ASFStorageStyle('Description') - ) - bpm = MediaField( - MP3StorageStyle('TBPM'), - MP4StorageStyle('tmpo', as_type=int), - StorageStyle('BPM'), - ASFStorageStyle('WM/BeatsPerMinute'), - out_type=int, - ) - comp = MediaField( - MP3StorageStyle('TCMP'), - MP4BoolStorageStyle('cpil'), - StorageStyle('COMPILATION'), - ASFStorageStyle('WM/IsCompilation', as_type=bool), - out_type=bool, - ) - albumartist = MediaField( - MP3StorageStyle('TPE2'), - MP4StorageStyle('aART'), - StorageStyle('ALBUM ARTIST'), - StorageStyle('ALBUMARTIST'), - ASFStorageStyle('WM/AlbumArtist'), - ) - albumtype = MediaField( - MP3DescStorageStyle(u'MusicBrainz Album Type'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Type'), - StorageStyle('MUSICBRAINZ_ALBUMTYPE'), - ASFStorageStyle('MusicBrainz/Album Type'), - ) - label = MediaField( - MP3StorageStyle('TPUB'), - MP4StorageStyle('----:com.apple.iTunes:Label'), - MP4StorageStyle('----:com.apple.iTunes:publisher'), - StorageStyle('LABEL'), - StorageStyle('PUBLISHER'), # Traktor - ASFStorageStyle('WM/Publisher'), - ) - artist_sort = MediaField( - MP3StorageStyle('TSOP'), - MP4StorageStyle('soar'), - StorageStyle('ARTISTSORT'), - ASFStorageStyle('WM/ArtistSortOrder'), - ) - albumartist_sort = MediaField( - MP3DescStorageStyle(u'ALBUMARTISTSORT'), - MP4StorageStyle('soaa'), - StorageStyle('ALBUMARTISTSORT'), - ASFStorageStyle('WM/AlbumArtistSortOrder'), - ) - asin = MediaField( - MP3DescStorageStyle(u'ASIN'), - MP4StorageStyle('----:com.apple.iTunes:ASIN'), - StorageStyle('ASIN'), - ASFStorageStyle('MusicBrainz/ASIN'), - ) - catalognum = MediaField( - MP3DescStorageStyle(u'CATALOGNUMBER'), - MP4StorageStyle('----:com.apple.iTunes:CATALOGNUMBER'), - StorageStyle('CATALOGNUMBER'), - ASFStorageStyle('WM/CatalogNo'), - ) - disctitle = MediaField( - MP3StorageStyle('TSST'), - MP4StorageStyle('----:com.apple.iTunes:DISCSUBTITLE'), - StorageStyle('DISCSUBTITLE'), - ASFStorageStyle('WM/SetSubTitle'), - ) - encoder = MediaField( - MP3StorageStyle('TENC'), - MP4StorageStyle('\xa9too'), - StorageStyle('ENCODEDBY'), - StorageStyle('ENCODER'), - ASFStorageStyle('WM/EncodedBy'), - ) - script = MediaField( - MP3DescStorageStyle(u'Script'), - MP4StorageStyle('----:com.apple.iTunes:SCRIPT'), - StorageStyle('SCRIPT'), - ASFStorageStyle('WM/Script'), - ) - language = MediaField( - MP3StorageStyle('TLAN'), - MP4StorageStyle('----:com.apple.iTunes:LANGUAGE'), - StorageStyle('LANGUAGE'), - ASFStorageStyle('WM/Language'), - ) - country = MediaField( - MP3DescStorageStyle(u'MusicBrainz Album Release Country'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz ' - 'Album Release Country'), - StorageStyle('RELEASECOUNTRY'), - ASFStorageStyle('MusicBrainz/Album Release Country'), - ) - albumstatus = MediaField( - MP3DescStorageStyle(u'MusicBrainz Album Status'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Status'), - StorageStyle('MUSICBRAINZ_ALBUMSTATUS'), - ASFStorageStyle('MusicBrainz/Album Status'), - ) - media = MediaField( - MP3StorageStyle('TMED'), - MP4StorageStyle('----:com.apple.iTunes:MEDIA'), - StorageStyle('MEDIA'), - ASFStorageStyle('WM/Media'), - ) - albumdisambig = MediaField( - # This tag mapping was invented for beets (not used by Picard, etc). - MP3DescStorageStyle(u'MusicBrainz Album Comment'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Comment'), - StorageStyle('MUSICBRAINZ_ALBUMCOMMENT'), - ASFStorageStyle('MusicBrainz/Album Comment'), - ) - - # Release date. - date = DateField( - MP3StorageStyle('TDRC'), - MP4StorageStyle('\xa9day'), - StorageStyle('DATE'), - ASFStorageStyle('WM/Year'), - year=(StorageStyle('YEAR'),)) - - year = date.year_field() - month = date.month_field() - day = date.day_field() - - # *Original* release date. - original_date = DateField( - MP3StorageStyle('TDOR'), - MP4StorageStyle('----:com.apple.iTunes:ORIGINAL YEAR'), - StorageStyle('ORIGINALDATE'), - ASFStorageStyle('WM/OriginalReleaseYear')) - - original_year = original_date.year_field() - original_month = original_date.month_field() - original_day = original_date.day_field() - - # Nonstandard metadata. - artist_credit = MediaField( - MP3DescStorageStyle(u'Artist Credit'), - MP4StorageStyle('----:com.apple.iTunes:Artist Credit'), - StorageStyle('ARTIST_CREDIT'), - ASFStorageStyle('beets/Artist Credit'), - ) - albumartist_credit = MediaField( - MP3DescStorageStyle(u'Album Artist Credit'), - MP4StorageStyle('----:com.apple.iTunes:Album Artist Credit'), - StorageStyle('ALBUMARTIST_CREDIT'), - ASFStorageStyle('beets/Album Artist Credit'), - ) - - # Legacy album art field - art = CoverArtField() - - # Image list - images = ImageListField() - - # MusicBrainz IDs. - mb_trackid = MediaField( - MP3UFIDStorageStyle(owner='http://musicbrainz.org'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Track Id'), - StorageStyle('MUSICBRAINZ_TRACKID'), - ASFStorageStyle('MusicBrainz/Track Id'), - ) - mb_releasetrackid = MediaField( - MP3DescStorageStyle(u'MusicBrainz Release Track Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Track Id'), - StorageStyle('MUSICBRAINZ_RELEASETRACKID'), - ASFStorageStyle('MusicBrainz/Release Track Id'), - ) - mb_albumid = MediaField( - MP3DescStorageStyle(u'MusicBrainz Album Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'), - StorageStyle('MUSICBRAINZ_ALBUMID'), - ASFStorageStyle('MusicBrainz/Album Id'), - ) - mb_artistid = MediaField( - MP3DescStorageStyle(u'MusicBrainz Artist Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Artist Id'), - StorageStyle('MUSICBRAINZ_ARTISTID'), - ASFStorageStyle('MusicBrainz/Artist Id'), - ) - mb_albumartistid = MediaField( - MP3DescStorageStyle(u'MusicBrainz Album Artist Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Artist Id'), - StorageStyle('MUSICBRAINZ_ALBUMARTISTID'), - ASFStorageStyle('MusicBrainz/Album Artist Id'), - ) - mb_releasegroupid = MediaField( - MP3DescStorageStyle(u'MusicBrainz Release Group Id'), - MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Group Id'), - StorageStyle('MUSICBRAINZ_RELEASEGROUPID'), - ASFStorageStyle('MusicBrainz/Release Group Id'), - ) - - # Acoustid fields. - acoustid_fingerprint = MediaField( - MP3DescStorageStyle(u'Acoustid Fingerprint'), - MP4StorageStyle('----:com.apple.iTunes:Acoustid Fingerprint'), - StorageStyle('ACOUSTID_FINGERPRINT'), - ASFStorageStyle('Acoustid/Fingerprint'), - ) - acoustid_id = MediaField( - MP3DescStorageStyle(u'Acoustid Id'), - MP4StorageStyle('----:com.apple.iTunes:Acoustid Id'), - StorageStyle('ACOUSTID_ID'), - ASFStorageStyle('Acoustid/Id'), - ) - - # ReplayGain fields. - rg_track_gain = MediaField( - MP3DescStorageStyle( - u'REPLAYGAIN_TRACK_GAIN', - float_places=2, suffix=u' dB' - ), - MP3DescStorageStyle( - u'replaygain_track_gain', - float_places=2, suffix=u' dB' - ), - MP3SoundCheckStorageStyle( - key='COMM', - index=0, desc=u'iTunNORM', - id3_lang='eng' - ), - MP4StorageStyle( - '----:com.apple.iTunes:replaygain_track_gain', - float_places=2, suffix=' dB' - ), - MP4SoundCheckStorageStyle( - '----:com.apple.iTunes:iTunNORM', - index=0 - ), - StorageStyle( - u'REPLAYGAIN_TRACK_GAIN', - float_places=2, suffix=u' dB' - ), - ASFStorageStyle( - u'replaygain_track_gain', - float_places=2, suffix=u' dB' - ), - out_type=float - ) - rg_album_gain = MediaField( - MP3DescStorageStyle( - u'REPLAYGAIN_ALBUM_GAIN', - float_places=2, suffix=u' dB' - ), - MP3DescStorageStyle( - u'replaygain_album_gain', - float_places=2, suffix=u' dB' - ), - MP4StorageStyle( - '----:com.apple.iTunes:replaygain_album_gain', - float_places=2, suffix=' dB' - ), - StorageStyle( - u'REPLAYGAIN_ALBUM_GAIN', - float_places=2, suffix=u' dB' - ), - ASFStorageStyle( - u'replaygain_album_gain', - float_places=2, suffix=u' dB' - ), - out_type=float - ) - rg_track_peak = MediaField( - MP3DescStorageStyle( - u'REPLAYGAIN_TRACK_PEAK', - float_places=6 - ), - MP3DescStorageStyle( - u'replaygain_track_peak', - float_places=6 - ), - MP3SoundCheckStorageStyle( - key=u'COMM', - index=1, desc=u'iTunNORM', - id3_lang='eng' - ), - MP4StorageStyle( - '----:com.apple.iTunes:replaygain_track_peak', - float_places=6 - ), - MP4SoundCheckStorageStyle( - '----:com.apple.iTunes:iTunNORM', - index=1 - ), - StorageStyle(u'REPLAYGAIN_TRACK_PEAK', float_places=6), - ASFStorageStyle(u'replaygain_track_peak', float_places=6), - out_type=float, - ) - rg_album_peak = MediaField( - MP3DescStorageStyle( - u'REPLAYGAIN_ALBUM_PEAK', - float_places=6 - ), - MP3DescStorageStyle( - u'replaygain_album_peak', - float_places=6 - ), - MP4StorageStyle( - '----:com.apple.iTunes:replaygain_album_peak', - float_places=6 - ), - StorageStyle(u'REPLAYGAIN_ALBUM_PEAK', float_places=6), - ASFStorageStyle(u'replaygain_album_peak', float_places=6), - out_type=float, - ) - - # EBU R128 fields. - r128_track_gain = MediaField( - MP3DescStorageStyle( - u'R128_TRACK_GAIN' - ), - MP4StorageStyle( - '----:com.apple.iTunes:R128_TRACK_GAIN' - ), - StorageStyle( - u'R128_TRACK_GAIN' - ), - ASFStorageStyle( - u'R128_TRACK_GAIN' - ), - out_type=int, - ) - r128_album_gain = MediaField( - MP3DescStorageStyle( - u'R128_ALBUM_GAIN' - ), - MP4StorageStyle( - '----:com.apple.iTunes:R128_ALBUM_GAIN' - ), - StorageStyle( - u'R128_ALBUM_GAIN' - ), - ASFStorageStyle( - u'R128_ALBUM_GAIN' - ), - out_type=int, - ) - - initial_key = MediaField( - MP3StorageStyle('TKEY'), - MP4StorageStyle('----:com.apple.iTunes:initialkey'), - StorageStyle('INITIALKEY'), - ASFStorageStyle('INITIALKEY'), - ) - - @property - def length(self): - """The duration of the audio in seconds (a float).""" - return self.mgfile.info.length - - @property - def samplerate(self): - """The audio's sample rate (an int).""" - if hasattr(self.mgfile.info, 'sample_rate'): - return self.mgfile.info.sample_rate - elif self.type == 'opus': - # Opus is always 48kHz internally. - return 48000 - return 0 - - @property - def bitdepth(self): - """The number of bits per sample in the audio encoding (an int). - Only available for certain file formats (zero where - unavailable). - """ - if hasattr(self.mgfile.info, 'bits_per_sample'): - return self.mgfile.info.bits_per_sample - return 0 - - @property - def channels(self): - """The number of channels in the audio (an int).""" - if hasattr(self.mgfile.info, 'channels'): - return self.mgfile.info.channels - return 0 - - @property - def bitrate(self): - """The number of bits per seconds used in the audio coding (an - int). If this is provided explicitly by the compressed file - format, this is a precise reflection of the encoding. Otherwise, - it is estimated from the on-disk file size. In this case, some - imprecision is possible because the file header is incorporated - in the file size. - """ - if hasattr(self.mgfile.info, 'bitrate') and self.mgfile.info.bitrate: - # Many formats provide it explicitly. - return self.mgfile.info.bitrate - else: - # Otherwise, we calculate bitrate from the file size. (This - # is the case for all of the lossless formats.) - if not self.length: - # Avoid division by zero if length is not available. - return 0 - size = os.path.getsize(self.path) - return int(size * 8 / self.length) - - @property - def format(self): - """A string describing the file format/codec.""" - return TYPES[self.type] +del key, value, warnings, mediafile diff --git a/libs/common/beets/plugins.py b/libs/common/beets/plugins.py index 1bd2cacd..ed1f82d8 100644 --- a/libs/common/beets/plugins.py +++ b/libs/common/beets/plugins.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,19 +14,19 @@ """Support for beets plugins.""" -from __future__ import division, absolute_import, print_function -import inspect import traceback import re +import inspect +import abc from collections import defaultdict from functools import wraps import beets from beets import logging -from beets import mediafile -import six +import mediafile + PLUGIN_NAMESPACE = 'beetsplug' @@ -50,26 +49,28 @@ class PluginLogFilter(logging.Filter): """A logging filter that identifies the plugin that emitted a log message. """ + def __init__(self, plugin): - self.prefix = u'{0}: '.format(plugin.name) + self.prefix = f'{plugin.name}: ' def filter(self, record): if hasattr(record.msg, 'msg') and isinstance(record.msg.msg, - six.string_types): + str): # A _LogMessage from our hacked-up Logging replacement. record.msg.msg = self.prefix + record.msg.msg - elif isinstance(record.msg, six.string_types): + elif isinstance(record.msg, str): record.msg = self.prefix + record.msg return True # Managing the plugins themselves. -class BeetsPlugin(object): +class BeetsPlugin: """The base class for all beets plugins. Plugins provide functionality by defining a subclass of BeetsPlugin and overriding the abstract methods defined here. """ + def __init__(self, name=None): """Perform one-time plugin setup. """ @@ -127,27 +128,24 @@ class BeetsPlugin(object): value after the function returns). Also determines which params may not be sent for backwards-compatibility. """ - argspec = inspect.getargspec(func) + argspec = inspect.getfullargspec(func) @wraps(func) def wrapper(*args, **kwargs): assert self._log.level == logging.NOTSET + verbosity = beets.config['verbose'].get(int) log_level = max(logging.DEBUG, base_log_level - 10 * verbosity) self._log.setLevel(log_level) + if argspec.varkw is None: + kwargs = {k: v for k, v in kwargs.items() + if k in argspec.args} + try: - try: - return func(*args, **kwargs) - except TypeError as exc: - if exc.args[0].startswith(func.__name__): - # caused by 'func' and not stuff internal to 'func' - kwargs = dict((arg, val) for arg, val in kwargs.items() - if arg in argspec.args) - return func(*args, **kwargs) - else: - raise + return func(*args, **kwargs) finally: self._log.setLevel(logging.NOTSET) + return wrapper def queries(self): @@ -167,7 +165,7 @@ class BeetsPlugin(object): """ return beets.autotag.hooks.Distance() - def candidates(self, items, artist, album, va_likely): + def candidates(self, items, artist, album, va_likely, extra_tags=None): """Should return a sequence of AlbumInfo objects that match the album whose items are provided. """ @@ -201,7 +199,7 @@ class BeetsPlugin(object): ``descriptor`` must be an instance of ``mediafile.MediaField``. """ - # Defer impor to prevent circular dependency + # Defer import to prevent circular dependency from beets import library mediafile.MediaFile.add_field(name, descriptor) library.Item._media_fields.add(name) @@ -264,14 +262,14 @@ def load_plugins(names=()): BeetsPlugin subclasses desired. """ for name in names: - modname = '{0}.{1}'.format(PLUGIN_NAMESPACE, name) + modname = f'{PLUGIN_NAMESPACE}.{name}' try: try: namespace = __import__(modname, None, None) except ImportError as exc: # Again, this is hacky: if exc.args[0].endswith(' ' + name): - log.warning(u'** plugin {0} not found', name) + log.warning('** plugin {0} not found', name) else: raise else: @@ -282,7 +280,7 @@ def load_plugins(names=()): except Exception: log.warning( - u'** error loading plugin {}:\n{}', + '** error loading plugin {}:\n{}', name, traceback.format_exc(), ) @@ -296,6 +294,11 @@ def find_plugins(): currently loaded beets plugins. Loads the default plugin set first. """ + if _instances: + # After the first call, use cached instances for performance reasons. + # See https://github.com/beetbox/beets/pull/3810 + return list(_instances.values()) + load_plugins() plugins = [] for cls in _classes: @@ -329,21 +332,31 @@ def queries(): def types(model_cls): # Gives us `item_types` and `album_types` - attr_name = '{0}_types'.format(model_cls.__name__.lower()) + attr_name = f'{model_cls.__name__.lower()}_types' types = {} for plugin in find_plugins(): plugin_types = getattr(plugin, attr_name, {}) for field in plugin_types: if field in types and plugin_types[field] != types[field]: raise PluginConflictException( - u'Plugin {0} defines flexible field {1} ' - u'which has already been defined with ' - u'another type.'.format(plugin.name, field) + 'Plugin {} defines flexible field {} ' + 'which has already been defined with ' + 'another type.'.format(plugin.name, field) ) types.update(plugin_types) return types +def named_queries(model_cls): + # Gather `item_queries` and `album_queries` from the plugins. + attr_name = f'{model_cls.__name__.lower()}_queries' + queries = {} + for plugin in find_plugins(): + plugin_queries = getattr(plugin, attr_name, {}) + queries.update(plugin_queries) + return queries + + def track_distance(item, info): """Gets the track distance calculated by all loaded plugins. Returns a Distance object. @@ -364,20 +377,19 @@ def album_distance(items, album_info, mapping): return dist -def candidates(items, artist, album, va_likely): +def candidates(items, artist, album, va_likely, extra_tags=None): """Gets MusicBrainz candidates for an album from each plugin. """ for plugin in find_plugins(): - for candidate in plugin.candidates(items, artist, album, va_likely): - yield candidate + yield from plugin.candidates(items, artist, album, va_likely, + extra_tags) def item_candidates(item, artist, title): """Gets MusicBrainz candidates for an item from the plugins. """ for plugin in find_plugins(): - for item_candidate in plugin.item_candidates(item, artist, title): - yield item_candidate + yield from plugin.item_candidates(item, artist, title) def album_for_id(album_id): @@ -470,7 +482,7 @@ def send(event, **arguments): Return a list of non-None values returned from the handlers. """ - log.debug(u'Sending event: {0}', event) + log.debug('Sending event: {0}', event) results = [] for handler in event_handlers()[event]: result = handler(**arguments) @@ -488,7 +500,7 @@ def feat_tokens(for_artist=True): feat_words = ['ft', 'featuring', 'feat', 'feat.', 'ft.'] if for_artist: feat_words += ['with', 'vs', 'and', 'con', '&'] - return '(?<=\s)(?:{0})(?=\s)'.format( + return r'(?<=\s)(?:{})(?=\s)'.format( '|'.join(re.escape(x) for x in feat_words) ) @@ -513,7 +525,7 @@ def sanitize_choices(choices, choices_all): def sanitize_pairs(pairs, pairs_all): """Clean up a single-element mapping configuration attribute as returned - by `confit`'s `Pairs` template: keep only two-element tuples present in + by Confuse's `Pairs` template: keep only two-element tuples present in pairs_all, remove duplicate elements, expand ('str', '*') and ('*', '*') wildcards while keeping the original order. Note that ('*', '*') and ('*', 'whatever') have the same effect. @@ -563,3 +575,188 @@ def notify_info_yielded(event): yield v return decorated return decorator + + +def get_distance(config, data_source, info): + """Returns the ``data_source`` weight and the maximum source weight + for albums or individual tracks. + """ + dist = beets.autotag.Distance() + if info.data_source == data_source: + dist.add('source', config['source_weight'].as_number()) + return dist + + +def apply_item_changes(lib, item, move, pretend, write): + """Store, move, and write the item according to the arguments. + + :param lib: beets library. + :type lib: beets.library.Library + :param item: Item whose changes to apply. + :type item: beets.library.Item + :param move: Move the item if it's in the library. + :type move: bool + :param pretend: Return without moving, writing, or storing the item's + metadata. + :type pretend: bool + :param write: Write the item's metadata to its media file. + :type write: bool + """ + if pretend: + return + + from beets import util + + # Move the item if it's in the library. + if move and lib.directory in util.ancestry(item.path): + item.move(with_album=False) + + if write: + item.try_write() + + item.store() + + +class MetadataSourcePlugin(metaclass=abc.ABCMeta): + def __init__(self): + super().__init__() + self.config.add({'source_weight': 0.5}) + + @abc.abstractproperty + def id_regex(self): + raise NotImplementedError + + @abc.abstractproperty + def data_source(self): + raise NotImplementedError + + @abc.abstractproperty + def search_url(self): + raise NotImplementedError + + @abc.abstractproperty + def album_url(self): + raise NotImplementedError + + @abc.abstractproperty + def track_url(self): + raise NotImplementedError + + @abc.abstractmethod + def _search_api(self, query_type, filters, keywords=''): + raise NotImplementedError + + @abc.abstractmethod + def album_for_id(self, album_id): + raise NotImplementedError + + @abc.abstractmethod + def track_for_id(self, track_id=None, track_data=None): + raise NotImplementedError + + @staticmethod + def get_artist(artists, id_key='id', name_key='name'): + """Returns an artist string (all artists) and an artist_id (the main + artist) for a list of artist object dicts. + + For each artist, this function moves articles (such as 'a', 'an', + and 'the') to the front and strips trailing disambiguation numbers. It + returns a tuple containing the comma-separated string of all + normalized artists and the ``id`` of the main/first artist. + + :param artists: Iterable of artist dicts or lists returned by API. + :type artists: list[dict] or list[list] + :param id_key: Key or index corresponding to the value of ``id`` for + the main/first artist. Defaults to 'id'. + :type id_key: str or int + :param name_key: Key or index corresponding to values of names + to concatenate for the artist string (containing all artists). + Defaults to 'name'. + :type name_key: str or int + :return: Normalized artist string. + :rtype: str + """ + artist_id = None + artist_names = [] + for artist in artists: + if not artist_id: + artist_id = artist[id_key] + name = artist[name_key] + # Strip disambiguation number. + name = re.sub(r' \(\d+\)$', '', name) + # Move articles to the front. + name = re.sub(r'^(.*?), (a|an|the)$', r'\2 \1', name, flags=re.I) + artist_names.append(name) + artist = ', '.join(artist_names).replace(' ,', ',') or None + return artist, artist_id + + def _get_id(self, url_type, id_): + """Parse an ID from its URL if necessary. + + :param url_type: Type of URL. Either 'album' or 'track'. + :type url_type: str + :param id_: Album/track ID or URL. + :type id_: str + :return: Album/track ID. + :rtype: str + """ + self._log.debug( + "Searching {} for {} '{}'", self.data_source, url_type, id_ + ) + match = re.search(self.id_regex['pattern'].format(url_type), str(id_)) + if match: + id_ = match.group(self.id_regex['match_group']) + if id_: + return id_ + return None + + def candidates(self, items, artist, album, va_likely, extra_tags=None): + """Returns a list of AlbumInfo objects for Search API results + matching an ``album`` and ``artist`` (if not various). + + :param items: List of items comprised by an album to be matched. + :type items: list[beets.library.Item] + :param artist: The artist of the album to be matched. + :type artist: str + :param album: The name of the album to be matched. + :type album: str + :param va_likely: True if the album to be matched likely has + Various Artists. + :type va_likely: bool + :return: Candidate AlbumInfo objects. + :rtype: list[beets.autotag.hooks.AlbumInfo] + """ + query_filters = {'album': album} + if not va_likely: + query_filters['artist'] = artist + results = self._search_api(query_type='album', filters=query_filters) + albums = [self.album_for_id(album_id=r['id']) for r in results] + return [a for a in albums if a is not None] + + def item_candidates(self, item, artist, title): + """Returns a list of TrackInfo objects for Search API results + matching ``title`` and ``artist``. + + :param item: Singleton item to be matched. + :type item: beets.library.Item + :param artist: The artist of the track to be matched. + :type artist: str + :param title: The title of the track to be matched. + :type title: str + :return: Candidate TrackInfo objects. + :rtype: list[beets.autotag.hooks.TrackInfo] + """ + tracks = self._search_api( + query_type='track', keywords=title, filters={'artist': artist} + ) + return [self.track_for_id(track_data=track) for track in tracks] + + def album_distance(self, items, album_info, mapping): + return get_distance( + data_source=self.data_source, info=album_info, config=self.config + ) + + def track_distance(self, item, track_info): + return get_distance( + data_source=self.data_source, info=track_info, config=self.config + ) diff --git a/libs/common/beets/random.py b/libs/common/beets/random.py new file mode 100644 index 00000000..eb4f55af --- /dev/null +++ b/libs/common/beets/random.py @@ -0,0 +1,113 @@ +# This file is part of beets. +# Copyright 2016, Philippe Mongeau. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Get a random song or album from the library. +""" + +import random +from operator import attrgetter +from itertools import groupby + + +def _length(obj, album): + """Get the duration of an item or album. + """ + if album: + return sum(i.length for i in obj.items()) + else: + return obj.length + + +def _equal_chance_permutation(objs, field='albumartist', random_gen=None): + """Generate (lazily) a permutation of the objects where every group + with equal values for `field` have an equal chance of appearing in + any given position. + """ + rand = random_gen or random + + # Group the objects by artist so we can sample from them. + key = attrgetter(field) + objs.sort(key=key) + objs_by_artists = {} + for artist, v in groupby(objs, key): + objs_by_artists[artist] = list(v) + + # While we still have artists with music to choose from, pick one + # randomly and pick a track from that artist. + while objs_by_artists: + # Choose an artist and an object for that artist, removing + # this choice from the pool. + artist = rand.choice(list(objs_by_artists.keys())) + objs_from_artist = objs_by_artists[artist] + i = rand.randint(0, len(objs_from_artist) - 1) + yield objs_from_artist.pop(i) + + # Remove the artist if we've used up all of its objects. + if not objs_from_artist: + del objs_by_artists[artist] + + +def _take(iter, num): + """Return a list containing the first `num` values in `iter` (or + fewer, if the iterable ends early). + """ + out = [] + for val in iter: + out.append(val) + num -= 1 + if num <= 0: + break + return out + + +def _take_time(iter, secs, album): + """Return a list containing the first values in `iter`, which should + be Item or Album objects, that add up to the given amount of time in + seconds. + """ + out = [] + total_time = 0.0 + for obj in iter: + length = _length(obj, album) + if total_time + length <= secs: + out.append(obj) + total_time += length + return out + + +def random_objs(objs, album, number=1, time=None, equal_chance=False, + random_gen=None): + """Get a random subset of the provided `objs`. + + If `number` is provided, produce that many matches. Otherwise, if + `time` is provided, instead select a list whose total time is close + to that number of minutes. If `equal_chance` is true, give each + artist an equal chance of being included so that artists with more + songs are not represented disproportionately. + """ + rand = random_gen or random + + # Permute the objects either in a straightforward way or an + # artist-balanced way. + if equal_chance: + perm = _equal_chance_permutation(objs) + else: + perm = objs + rand.shuffle(perm) # N.B. This shuffles the original list. + + # Select objects by time our count. + if time: + return _take_time(perm, time * 60, album) + else: + return _take(perm, number) diff --git a/libs/common/beets/ui/__init__.py b/libs/common/beets/ui/__init__.py index af2b79a1..121cb5dc 100644 --- a/libs/common/beets/ui/__init__.py +++ b/libs/common/beets/ui/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -18,7 +17,6 @@ interface. To invoke the CLI, just call beets.ui.main(). The actual CLI commands are implemented in the ui.commands module. """ -from __future__ import division, absolute_import, print_function import optparse import textwrap @@ -30,19 +28,18 @@ import re import struct import traceback import os.path -from six.moves import input from beets import logging from beets import library from beets import plugins from beets import util -from beets.util.functemplate import Template +from beets.util.functemplate import template from beets import config -from beets.util import confit, as_string +from beets.util import as_string from beets.autotag import mb from beets.dbcore import query as db_query from beets.dbcore import db -import six +import confuse # On Windows platforms, use colorama to support "ANSI" terminal colors. if sys.platform == 'win32': @@ -61,8 +58,8 @@ log.propagate = False # Don't propagate to root handler. PF_KEY_QUERIES = { - 'comp': u'comp:true', - 'singleton': u'singleton:true', + 'comp': 'comp:true', + 'singleton': 'singleton:true', } @@ -112,10 +109,7 @@ def decargs(arglist): """Given a list of command-line argument bytestrings, attempts to decode them to Unicode strings when running under Python 2. """ - if six.PY2: - return [s.decode(util.arg_encoding()) for s in arglist] - else: - return arglist + return arglist def print_(*strings, **kwargs): @@ -130,30 +124,25 @@ def print_(*strings, **kwargs): (it defaults to a newline). """ if not strings: - strings = [u''] - assert isinstance(strings[0], six.text_type) + strings = [''] + assert isinstance(strings[0], str) - txt = u' '.join(strings) - txt += kwargs.get('end', u'\n') + txt = ' '.join(strings) + txt += kwargs.get('end', '\n') # Encode the string and write it to stdout. - if six.PY2: - # On Python 2, sys.stdout expects bytes. + # On Python 3, sys.stdout expects text strings and uses the + # exception-throwing encoding error policy. To avoid throwing + # errors and use our configurable encoding override, we use the + # underlying bytes buffer instead. + if hasattr(sys.stdout, 'buffer'): out = txt.encode(_out_encoding(), 'replace') - sys.stdout.write(out) + sys.stdout.buffer.write(out) + sys.stdout.buffer.flush() else: - # On Python 3, sys.stdout expects text strings and uses the - # exception-throwing encoding error policy. To avoid throwing - # errors and use our configurable encoding override, we use the - # underlying bytes buffer instead. - if hasattr(sys.stdout, 'buffer'): - out = txt.encode(_out_encoding(), 'replace') - sys.stdout.buffer.write(out) - sys.stdout.buffer.flush() - else: - # In our test harnesses (e.g., DummyOut), sys.stdout.buffer - # does not exist. We instead just record the text string. - sys.stdout.write(txt) + # In our test harnesses (e.g., DummyOut), sys.stdout.buffer + # does not exist. We instead just record the text string. + sys.stdout.write(txt) # Configuration wrappers. @@ -203,19 +192,16 @@ def input_(prompt=None): """ # raw_input incorrectly sends prompts to stderr, not stdout, so we # use print_() explicitly to display prompts. - # http://bugs.python.org/issue1927 + # https://bugs.python.org/issue1927 if prompt: - print_(prompt, end=u' ') + print_(prompt, end=' ') try: resp = input() except EOFError: - raise UserError(u'stdin stream ended while input required') + raise UserError('stdin stream ended while input required') - if six.PY2: - return resp.decode(_in_encoding(), 'ignore') - else: - return resp + return resp def input_options(options, require=False, prompt=None, fallback_prompt=None, @@ -259,7 +245,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, found_letter = letter break else: - raise ValueError(u'no unambiguous lettering found') + raise ValueError('no unambiguous lettering found') letters[found_letter.lower()] = option index = option.index(found_letter) @@ -267,7 +253,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, # Mark the option's shortcut letter for display. if not require and ( (default is None and not numrange and first) or - (isinstance(default, six.string_types) and + (isinstance(default, str) and found_letter.lower() == default.lower())): # The first option is the default; mark it. show_letter = '[%s]' % found_letter.upper() @@ -303,11 +289,11 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, prompt_part_lengths = [] if numrange: if isinstance(default, int): - default_name = six.text_type(default) + default_name = str(default) default_name = colorize('action_default', default_name) tmpl = '# selection (default %s)' prompt_parts.append(tmpl % default_name) - prompt_part_lengths.append(len(tmpl % six.text_type(default))) + prompt_part_lengths.append(len(tmpl % str(default))) else: prompt_parts.append('# selection') prompt_part_lengths.append(len(prompt_parts[-1])) @@ -342,9 +328,9 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, # Make a fallback prompt too. This is displayed if the user enters # something that is not recognized. if not fallback_prompt: - fallback_prompt = u'Enter one of ' + fallback_prompt = 'Enter one of ' if numrange: - fallback_prompt += u'%i-%i, ' % numrange + fallback_prompt += '%i-%i, ' % numrange fallback_prompt += ', '.join(display_letters) + ':' resp = input_(prompt) @@ -383,34 +369,41 @@ def input_yn(prompt, require=False): "yes" unless `require` is `True`, in which case there is no default. """ sel = input_options( - ('y', 'n'), require, prompt, u'Enter Y or N:' + ('y', 'n'), require, prompt, 'Enter Y or N:' ) - return sel == u'y' + return sel == 'y' -def input_select_objects(prompt, objs, rep): +def input_select_objects(prompt, objs, rep, prompt_all=None): """Prompt to user to choose all, none, or some of the given objects. Return the list of selected objects. `prompt` is the prompt string to use for each question (it should be - phrased as an imperative verb). `rep` is a function to call on each - object to print it out when confirming objects individually. + phrased as an imperative verb). If `prompt_all` is given, it is used + instead of `prompt` for the first (yes(/no/select) question. + `rep` is a function to call on each object to print it out when confirming + objects individually. """ choice = input_options( - (u'y', u'n', u's'), False, - u'%s? (Yes/no/select)' % prompt) + ('y', 'n', 's'), False, + '%s? (Yes/no/select)' % (prompt_all or prompt)) print() # Blank line. - if choice == u'y': # Yes. + if choice == 'y': # Yes. return objs - elif choice == u's': # Select. + elif choice == 's': # Select. out = [] for obj in objs: rep(obj) - if input_yn(u'%s? (yes/no)' % prompt, True): + answer = input_options( + ('y', 'n', 'q'), True, '%s? (yes/no/quit)' % prompt, + 'Enter Y or N:' + ) + if answer == 'y': out.append(obj) - print() # go to a new line + elif answer == 'q': + return out return out else: # No. @@ -421,14 +414,14 @@ def input_select_objects(prompt, objs, rep): def human_bytes(size): """Formats size, a number of bytes, in a human-readable way.""" - powers = [u'', u'K', u'M', u'G', u'T', u'P', u'E', u'Z', u'Y', u'H'] + powers = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'H'] unit = 'B' for power in powers: if size < 1024: - return u"%3.1f %s%s" % (size, power, unit) + return f"{size:3.1f} {power}{unit}" size /= 1024.0 - unit = u'iB' - return u"big" + unit = 'iB' + return "big" def human_seconds(interval): @@ -436,13 +429,13 @@ def human_seconds(interval): interval using English words. """ units = [ - (1, u'second'), - (60, u'minute'), - (60, u'hour'), - (24, u'day'), - (7, u'week'), - (52, u'year'), - (10, u'decade'), + (1, 'second'), + (60, 'minute'), + (60, 'hour'), + (24, 'day'), + (7, 'week'), + (52, 'year'), + (10, 'decade'), ] for i in range(len(units) - 1): increment, suffix = units[i] @@ -455,7 +448,7 @@ def human_seconds(interval): increment, suffix = units[-1] interval /= float(increment) - return u"%3.1f %ss" % (interval, suffix) + return f"{interval:3.1f} {suffix}s" def human_seconds_short(interval): @@ -463,13 +456,13 @@ def human_seconds_short(interval): string. """ interval = int(interval) - return u'%i:%02i' % (interval // 60, interval % 60) + return '%i:%02i' % (interval // 60, interval % 60) # Colorization. # ANSI terminal colorization code heavily inspired by pygments: -# http://dev.pocoo.org/hg/pygments-main/file/b2deea5b5030/pygments/console.py +# https://bitbucket.org/birkenfeld/pygments-main/src/default/pygments/console.py # (pygments is by Tim Hatch, Armin Ronacher, et al.) COLOR_ESCAPE = "\x1b[" DARK_COLORS = { @@ -516,7 +509,7 @@ def _colorize(color, text): elif color in LIGHT_COLORS: escape = COLOR_ESCAPE + "%i;01m" % (LIGHT_COLORS[color] + 30) else: - raise ValueError(u'no such color %s', color) + raise ValueError('no such color %s', color) return escape + text + RESET_COLOR @@ -524,22 +517,22 @@ def colorize(color_name, text): """Colorize text if colored output is enabled. (Like _colorize but conditional.) """ - if config['ui']['color']: - global COLORS - if not COLORS: - COLORS = dict((name, - config['ui']['colors'][name].as_str()) - for name in COLOR_NAMES) - # In case a 3rd party plugin is still passing the actual color ('red') - # instead of the abstract color name ('text_error') - color = COLORS.get(color_name) - if not color: - log.debug(u'Invalid color_name: {0}', color_name) - color = color_name - return _colorize(color, text) - else: + if not config['ui']['color'] or 'NO_COLOR' in os.environ.keys(): return text + global COLORS + if not COLORS: + COLORS = {name: + config['ui']['colors'][name].as_str() + for name in COLOR_NAMES} + # In case a 3rd party plugin is still passing the actual color ('red') + # instead of the abstract color name ('text_error') + color = COLORS.get(color_name) + if not color: + log.debug('Invalid color_name: {0}', color_name) + color = color_name + return _colorize(color, text) + def _colordiff(a, b, highlight='text_highlight', minor_highlight='text_highlight_minor'): @@ -548,11 +541,11 @@ def _colordiff(a, b, highlight='text_highlight', highlighted intelligently to show differences; other values are stringified and highlighted in their entirety. """ - if not isinstance(a, six.string_types) \ - or not isinstance(b, six.string_types): + if not isinstance(a, str) \ + or not isinstance(b, str): # Non-strings: use ordinary equality. - a = six.text_type(a) - b = six.text_type(b) + a = str(a) + b = str(b) if a == b: return a, b else: @@ -590,7 +583,7 @@ def _colordiff(a, b, highlight='text_highlight', else: assert(False) - return u''.join(a_out), u''.join(b_out) + return ''.join(a_out), ''.join(b_out) def colordiff(a, b, highlight='text_highlight'): @@ -600,7 +593,7 @@ def colordiff(a, b, highlight='text_highlight'): if config['ui']['color']: return _colordiff(a, b, highlight) else: - return six.text_type(a), six.text_type(b) + return str(a), str(b) def get_path_formats(subview=None): @@ -611,12 +604,12 @@ def get_path_formats(subview=None): subview = subview or config['paths'] for query, view in subview.items(): query = PF_KEY_QUERIES.get(query, query) # Expand common queries. - path_formats.append((query, Template(view.as_str()))) + path_formats.append((query, template(view.as_str()))) return path_formats def get_replacements(): - """Confit validation function that reads regex/string pairs. + """Confuse validation function that reads regex/string pairs. """ replacements = [] for pattern, repl in config['replace'].get(dict).items(): @@ -625,7 +618,7 @@ def get_replacements(): replacements.append((re.compile(pattern), repl)) except re.error: raise UserError( - u'malformed regular expression in replace: {0}'.format( + 'malformed regular expression in replace: {}'.format( pattern ) ) @@ -646,7 +639,7 @@ def term_width(): try: buf = fcntl.ioctl(0, termios.TIOCGWINSZ, ' ' * 4) - except IOError: + except OSError: return fallback try: height, width = struct.unpack('hh', buf) @@ -658,10 +651,10 @@ def term_width(): FLOAT_EPSILON = 0.01 -def _field_diff(field, old, new): - """Given two Model objects, format their values for `field` and - highlight changes among them. Return a human-readable string. If the - value has not changed, return None instead. +def _field_diff(field, old, old_fmt, new, new_fmt): + """Given two Model objects and their formatted views, format their values + for `field` and highlight changes among them. Return a human-readable + string. If the value has not changed, return None instead. """ oldval = old.get(field) newval = new.get(field) @@ -674,18 +667,18 @@ def _field_diff(field, old, new): return None # Get formatted values for output. - oldstr = old.formatted().get(field, u'') - newstr = new.formatted().get(field, u'') + oldstr = old_fmt.get(field, '') + newstr = new_fmt.get(field, '') # For strings, highlight changes. For others, colorize the whole # thing. - if isinstance(oldval, six.string_types): + if isinstance(oldval, str): oldstr, newstr = colordiff(oldval, newstr) else: oldstr = colorize('text_error', oldstr) newstr = colorize('text_error', newstr) - return u'{0} -> {1}'.format(oldstr, newstr) + return f'{oldstr} -> {newstr}' def show_model_changes(new, old=None, fields=None, always=False): @@ -700,6 +693,11 @@ def show_model_changes(new, old=None, fields=None, always=False): """ old = old or new._db._get(type(new), new.id) + # Keep the formatted views around instead of re-creating them in each + # iteration step + old_fmt = old.formatted() + new_fmt = new.formatted() + # Build up lines showing changed fields. changes = [] for field in old: @@ -708,25 +706,25 @@ def show_model_changes(new, old=None, fields=None, always=False): continue # Detect and show difference for this field. - line = _field_diff(field, old, new) + line = _field_diff(field, old, old_fmt, new, new_fmt) if line: - changes.append(u' {0}: {1}'.format(field, line)) + changes.append(f' {field}: {line}') # New fields. for field in set(new) - set(old): if fields and field not in fields: continue - changes.append(u' {0}: {1}'.format( + changes.append(' {}: {}'.format( field, - colorize('text_highlight', new.formatted()[field]) + colorize('text_highlight', new_fmt[field]) )) # Print changes. if changes or always: print_(format(old)) if changes: - print_(u'\n'.join(changes)) + print_('\n'.join(changes)) return bool(changes) @@ -759,15 +757,21 @@ def show_path_changes(path_changes): if max_width > col_width: # Print every change over two lines for source, dest in zip(sources, destinations): - log.info(u'{0} \n -> {1}', source, dest) + color_source, color_dest = colordiff(source, dest) + print_('{0} \n -> {1}'.format(color_source, color_dest)) else: # Print every change on a single line, and add a header title_pad = max_width - len('Source ') + len(' -> ') - log.info(u'Source {0} Destination', ' ' * title_pad) + print_('Source {0} Destination'.format(' ' * title_pad)) for source, dest in zip(sources, destinations): pad = max_width - len(source) - log.info(u'{0} {1} -> {2}', source, ' ' * pad, dest) + color_source, color_dest = colordiff(source, dest) + print_('{0} {1} -> {2}'.format( + color_source, + ' ' * pad, + color_dest, + )) # Helper functions for option parsing. @@ -783,22 +787,25 @@ def _store_dict(option, opt_str, value, parser): if option_values is None: # This is the first supplied ``key=value`` pair of option. # Initialize empty dictionary and get a reference to it. - setattr(parser.values, dest, dict()) + setattr(parser.values, dest, {}) option_values = getattr(parser.values, dest) + # Decode the argument using the platform's argument encoding. + value = util.text_string(value, util.arg_encoding()) + try: - key, value = map(lambda s: util.text_string(s), value.split('=')) + key, value = value.split('=', 1) if not (key and value): raise ValueError except ValueError: raise UserError( - "supplied argument `{0}' is not of the form `key=value'" + "supplied argument `{}' is not of the form `key=value'" .format(value)) option_values[key] = value -class CommonOptionsParser(optparse.OptionParser, object): +class CommonOptionsParser(optparse.OptionParser): """Offers a simple way to add common formatting options. Options available include: @@ -813,8 +820,9 @@ class CommonOptionsParser(optparse.OptionParser, object): Each method is fully documented in the related method. """ + def __init__(self, *args, **kwargs): - super(CommonOptionsParser, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._album_flags = False # this serves both as an indicator that we offer the feature AND allows # us to check whether it has been specified on the CLI - bypassing the @@ -828,7 +836,7 @@ class CommonOptionsParser(optparse.OptionParser, object): Sets the album property on the options extracted from the CLI. """ album = optparse.Option(*flags, action='store_true', - help=u'match albums instead of tracks') + help='match albums instead of tracks') self.add_option(album) self._album_flags = set(flags) @@ -846,7 +854,7 @@ class CommonOptionsParser(optparse.OptionParser, object): elif value: value, = decargs([value]) else: - value = u'' + value = '' parser.values.format = value if target: @@ -873,14 +881,14 @@ class CommonOptionsParser(optparse.OptionParser, object): By default this affects both items and albums. If add_album_option() is used then the target will be autodetected. - Sets the format property to u'$path' on the options extracted from the + Sets the format property to '$path' on the options extracted from the CLI. """ path = optparse.Option(*flags, nargs=0, action='callback', callback=self._set_format, - callback_kwargs={'fmt': u'$path', + callback_kwargs={'fmt': '$path', 'store_true': True}, - help=u'print paths for matched items or albums') + help='print paths for matched items or albums') self.add_option(path) def add_format_option(self, flags=('-f', '--format'), target=None): @@ -900,7 +908,7 @@ class CommonOptionsParser(optparse.OptionParser, object): """ kwargs = {} if target: - if isinstance(target, six.string_types): + if isinstance(target, str): target = {'item': library.Item, 'album': library.Album}[target] kwargs['target'] = target @@ -908,7 +916,7 @@ class CommonOptionsParser(optparse.OptionParser, object): opt = optparse.Option(*flags, action='callback', callback=self._set_format, callback_kwargs=kwargs, - help=u'print with custom format') + help='print with custom format') self.add_option(opt) def add_all_common_options(self): @@ -923,14 +931,15 @@ class CommonOptionsParser(optparse.OptionParser, object): # # This is a fairly generic subcommand parser for optparse. It is # maintained externally here: -# http://gist.github.com/462717 +# https://gist.github.com/462717 # There you will also find a better description of the code and a more # succinct example program. -class Subcommand(object): +class Subcommand: """A subcommand of a root command-line application that may be invoked by a SubcommandOptionParser. """ + def __init__(self, name, parser=None, help='', aliases=(), hide=False): """Creates a new subcommand. name is the primary way to invoke the subcommand; aliases are alternate names. parser is an @@ -958,7 +967,7 @@ class Subcommand(object): @root_parser.setter def root_parser(self, root_parser): self._root_parser = root_parser - self.parser.prog = '{0} {1}'.format( + self.parser.prog = '{} {}'.format( as_string(root_parser.get_prog_name()), self.name) @@ -974,13 +983,13 @@ class SubcommandsOptionParser(CommonOptionsParser): """ # A more helpful default usage. if 'usage' not in kwargs: - kwargs['usage'] = u""" + kwargs['usage'] = """ %prog COMMAND [ARGS...] %prog help COMMAND""" kwargs['add_help_option'] = False # Super constructor. - super(SubcommandsOptionParser, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Our root parser needs to stop on the first unrecognized argument. self.disable_interspersed_args() @@ -997,7 +1006,7 @@ class SubcommandsOptionParser(CommonOptionsParser): # Add the list of subcommands to the help message. def format_help(self, formatter=None): # Get the original help message, to which we will append. - out = super(SubcommandsOptionParser, self).format_help(formatter) + out = super().format_help(formatter) if formatter is None: formatter = self.formatter @@ -1083,7 +1092,7 @@ class SubcommandsOptionParser(CommonOptionsParser): cmdname = args.pop(0) subcommand = self._subcommand_for_name(cmdname) if not subcommand: - raise UserError(u"unknown command '{0}'".format(cmdname)) + raise UserError(f"unknown command '{cmdname}'") suboptions, subargs = subcommand.parse_args(args) return subcommand, suboptions, subargs @@ -1094,26 +1103,32 @@ optparse.Option.ALWAYS_TYPED_ACTIONS += ('callback',) # The main entry point and bootstrapping. -def _load_plugins(config): - """Load the plugins specified in the configuration. +def _load_plugins(options, config): + """Load the plugins specified on the command line or in the configuration. """ paths = config['pluginpath'].as_str_seq(split=False) paths = [util.normpath(p) for p in paths] - log.debug(u'plugin paths: {0}', util.displayable_path(paths)) + log.debug('plugin paths: {0}', util.displayable_path(paths)) # On Python 3, the search paths need to be unicode. paths = [util.py3_path(p) for p in paths] # Extend the `beetsplug` package to include the plugin paths. import beetsplug - beetsplug.__path__ = paths + beetsplug.__path__ + beetsplug.__path__ = paths + list(beetsplug.__path__) # For backwards compatibility, also support plugin paths that # *contain* a `beetsplug` package. sys.path += paths - plugins.load_plugins(config['plugins'].as_str_seq()) - plugins.send("pluginload") + # If we were given any plugins on the command line, use those. + if options.plugins is not None: + plugin_list = (options.plugins.split(',') + if len(options.plugins) > 0 else []) + else: + plugin_list = config['plugins'].as_str_seq() + + plugins.load_plugins(plugin_list) return plugins @@ -1127,7 +1142,20 @@ def _setup(options, lib=None): config = _configure(options) - plugins = _load_plugins(config) + plugins = _load_plugins(options, config) + + # Add types and queries defined by plugins. + plugin_types_album = plugins.types(library.Album) + library.Album._types.update(plugin_types_album) + item_types = plugin_types_album.copy() + item_types.update(library.Item._types) + item_types.update(plugins.types(library.Item)) + library.Item._types = item_types + + library.Item._queries.update(plugins.named_queries(library.Item)) + library.Album._queries.update(plugins.named_queries(library.Album)) + + plugins.send("pluginload") # Get the default subcommands. from beets.ui.commands import default_commands @@ -1138,8 +1166,6 @@ def _setup(options, lib=None): if lib is None: lib = _open_library(config) plugins.send("library_opened", lib=lib) - library.Item._types.update(plugins.types(library.Item)) - library.Album._types.update(plugins.types(library.Album)) return subcommands, plugins, lib @@ -1165,18 +1191,18 @@ def _configure(options): log.set_global_level(logging.INFO) if overlay_path: - log.debug(u'overlaying configuration: {0}', + log.debug('overlaying configuration: {0}', util.displayable_path(overlay_path)) config_path = config.user_config_path() if os.path.isfile(config_path): - log.debug(u'user configuration: {0}', + log.debug('user configuration: {0}', util.displayable_path(config_path)) else: - log.debug(u'no user configuration found at {0}', + log.debug('no user configuration found at {0}', util.displayable_path(config_path)) - log.debug(u'data directory: {0}', + log.debug('data directory: {0}', util.displayable_path(config.config_dir())) return config @@ -1193,13 +1219,14 @@ def _open_library(config): get_replacements(), ) lib.get_item(0) # Test database connection. - except (sqlite3.OperationalError, sqlite3.DatabaseError): - log.debug(u'{}', traceback.format_exc()) - raise UserError(u"database file {0} could not be opened".format( - util.displayable_path(dbpath) + except (sqlite3.OperationalError, sqlite3.DatabaseError) as db_error: + log.debug('{}', traceback.format_exc()) + raise UserError("database file {} cannot not be opened: {}".format( + util.displayable_path(dbpath), + db_error )) - log.debug(u'library database: {0}\n' - u'library directory: {1}', + log.debug('library database: {0}\n' + 'library directory: {1}', util.displayable_path(lib.path), util.displayable_path(lib.directory)) return lib @@ -1213,15 +1240,17 @@ def _raw_main(args, lib=None): parser.add_format_option(flags=('--format-item',), target=library.Item) parser.add_format_option(flags=('--format-album',), target=library.Album) parser.add_option('-l', '--library', dest='library', - help=u'library database file to use') + help='library database file to use') parser.add_option('-d', '--directory', dest='directory', - help=u"destination music directory") + help="destination music directory") parser.add_option('-v', '--verbose', dest='verbose', action='count', - help=u'log more details (use twice for even more)') + help='log more details (use twice for even more)') parser.add_option('-c', '--config', dest='config', - help=u'path to configuration file') + help='path to configuration file') + parser.add_option('-p', '--plugins', dest='plugins', + help='a comma-separated list of plugins to load') parser.add_option('-h', '--help', dest='help', action='store_true', - help=u'show this help message and exit') + help='show this help message and exit') parser.add_option('--version', dest='version', action='store_true', help=optparse.SUPPRESS_HELP) @@ -1256,7 +1285,7 @@ def main(args=None): _raw_main(args) except UserError as exc: message = exc.args[0] if exc.args else None - log.error(u'error: {0}', message) + log.error('error: {0}', message) sys.exit(1) except util.HumanReadableException as exc: exc.log(log) @@ -1267,13 +1296,13 @@ def main(args=None): log.debug('{}', traceback.format_exc()) log.error('{}', exc) sys.exit(1) - except confit.ConfigError as exc: - log.error(u'configuration error: {0}', exc) + except confuse.ConfigError as exc: + log.error('configuration error: {0}', exc) sys.exit(1) except db_query.InvalidQueryError as exc: - log.error(u'invalid query: {0}', exc) + log.error('invalid query: {0}', exc) sys.exit(1) - except IOError as exc: + except OSError as exc: if exc.errno == errno.EPIPE: # "Broken pipe". End silently. sys.stderr.close() @@ -1281,11 +1310,11 @@ def main(args=None): raise except KeyboardInterrupt: # Silently ignore ^C except in verbose mode. - log.debug(u'{}', traceback.format_exc()) + log.debug('{}', traceback.format_exc()) except db.DBAccessError as exc: log.error( - u'database access error: {0}\n' - u'the library file might have a permissions problem', + 'database access error: {0}\n' + 'the library file might have a permissions problem', exc ) sys.exit(1) diff --git a/libs/common/beets/ui/commands.py b/libs/common/beets/ui/commands.py index 46ae1d93..3a337401 100644 --- a/libs/common/beets/ui/commands.py +++ b/libs/common/beets/ui/commands.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -17,7 +16,6 @@ interface. """ -from __future__ import division, absolute_import, print_function import os import re @@ -39,11 +37,10 @@ from beets.util import syspath, normpath, ancestry, displayable_path, \ from beets import library from beets import config from beets import logging -from beets.util.confit import _package_path -import six + from . import _store_dict -VARIOUS_ARTISTS = u'Various Artists' +VARIOUS_ARTISTS = 'Various Artists' PromptChoice = namedtuple('PromptChoice', ['short', 'long', 'callback']) # Global logger. @@ -75,9 +72,9 @@ def _do_query(lib, query, album, also_items=True): items = list(lib.items(query)) if album and not albums: - raise ui.UserError(u'No matching albums found.') + raise ui.UserError('No matching albums found.') elif not album and not items: - raise ui.UserError(u'No matching items found.') + raise ui.UserError('No matching items found.') return items, albums @@ -89,33 +86,34 @@ def _print_keys(query): returned row, with indentation of 2 spaces. """ for row in query: - print_(u' ' * 2 + row['key']) + print_(' ' * 2 + row['key']) def fields_func(lib, opts, args): def _print_rows(names): names.sort() - print_(u' ' + u'\n '.join(names)) + print_(' ' + '\n '.join(names)) - print_(u"Item fields:") + print_("Item fields:") _print_rows(library.Item.all_keys()) - print_(u"Album fields:") + print_("Album fields:") _print_rows(library.Album.all_keys()) with lib.transaction() as tx: # The SQL uses the DISTINCT to get unique values from the query unique_fields = 'SELECT DISTINCT key FROM (%s)' - print_(u"Item flexible attributes:") + print_("Item flexible attributes:") _print_keys(tx.query(unique_fields % library.Item._flex_table)) - print_(u"Album flexible attributes:") + print_("Album flexible attributes:") _print_keys(tx.query(unique_fields % library.Album._flex_table)) + fields_cmd = ui.Subcommand( 'fields', - help=u'show fields available for queries and format strings' + help='show fields available for queries and format strings' ) fields_cmd.func = fields_func default_commands.append(fields_cmd) @@ -126,9 +124,9 @@ default_commands.append(fields_cmd) class HelpCommand(ui.Subcommand): def __init__(self): - super(HelpCommand, self).__init__( + super().__init__( 'help', aliases=('?',), - help=u'give detailed help on a specific sub-command', + help='give detailed help on a specific sub-command', ) def func(self, lib, opts, args): @@ -136,7 +134,7 @@ class HelpCommand(ui.Subcommand): cmdname = args[0] helpcommand = self.root_parser._subcommand_for_name(cmdname) if not helpcommand: - raise ui.UserError(u"unknown command '{0}'".format(cmdname)) + raise ui.UserError(f"unknown command '{cmdname}'") helpcommand.print_help() else: self.root_parser.print_help() @@ -161,29 +159,31 @@ def disambig_string(info): if isinstance(info, hooks.AlbumInfo): if info.media: if info.mediums and info.mediums > 1: - disambig.append(u'{0}x{1}'.format( + disambig.append('{}x{}'.format( info.mediums, info.media )) else: disambig.append(info.media) if info.year: - disambig.append(six.text_type(info.year)) + disambig.append(str(info.year)) if info.country: disambig.append(info.country) if info.label: disambig.append(info.label) + if info.catalognum: + disambig.append(info.catalognum) if info.albumdisambig: disambig.append(info.albumdisambig) if disambig: - return u', '.join(disambig) + return ', '.join(disambig) def dist_string(dist): """Formats a distance (a float) as a colorized similarity percentage string. """ - out = u'%.1f%%' % ((1 - dist) * 100) + out = '%.1f%%' % ((1 - dist) * 100) if dist <= config['match']['strong_rec_thresh'].as_number(): out = ui.colorize('text_success', out) elif dist <= config['match']['medium_rec_thresh'].as_number(): @@ -206,7 +206,7 @@ def penalty_string(distance, limit=None): if penalties: if limit and len(penalties) > limit: penalties = penalties[:limit] + ['...'] - return ui.colorize('text_warning', u'(%s)' % ', '.join(penalties)) + return ui.colorize('text_warning', '(%s)' % ', '.join(penalties)) def show_change(cur_artist, cur_album, match): @@ -216,11 +216,11 @@ def show_change(cur_artist, cur_album, match): """ def show_album(artist, album): if artist: - album_description = u' %s - %s' % (artist, album) + album_description = f' {artist} - {album}' elif album: - album_description = u' %s' % album + album_description = ' %s' % album else: - album_description = u' (unknown album)' + album_description = ' (unknown album)' print_(album_description) def format_index(track_info): @@ -238,40 +238,44 @@ def show_change(cur_artist, cur_album, match): mediums = track_info.disctotal if config['per_disc_numbering']: if mediums and mediums > 1: - return u'{0}-{1}'.format(medium, medium_index) + return f'{medium}-{medium_index}' else: - return six.text_type(medium_index or index) + return str(medium_index if medium_index is not None + else index) else: - return six.text_type(index) + return str(index) # Identify the album in question. if cur_artist != match.info.artist or \ (cur_album != match.info.album and match.info.album != VARIOUS_ARTISTS): artist_l, artist_r = cur_artist or '', match.info.artist - album_l, album_r = cur_album or '', match.info.album + album_l, album_r = cur_album or '', match.info.album if artist_r == VARIOUS_ARTISTS: # Hide artists for VA releases. - artist_l, artist_r = u'', u'' + artist_l, artist_r = '', '' + + if config['artist_credit']: + artist_r = match.info.artist_credit artist_l, artist_r = ui.colordiff(artist_l, artist_r) album_l, album_r = ui.colordiff(album_l, album_r) - print_(u"Correcting tags from:") + print_("Correcting tags from:") show_album(artist_l, album_l) - print_(u"To:") + print_("To:") show_album(artist_r, album_r) else: - print_(u"Tagging:\n {0.artist} - {0.album}".format(match.info)) + print_("Tagging:\n {0.artist} - {0.album}".format(match.info)) # Data URL. if match.info.data_url: - print_(u'URL:\n %s' % match.info.data_url) + print_('URL:\n %s' % match.info.data_url) # Info line. info = [] # Similarity. - info.append(u'(Similarity: %s)' % dist_string(match.distance)) + info.append('(Similarity: %s)' % dist_string(match.distance)) # Penalties. penalties = penalty_string(match.distance) if penalties: @@ -279,7 +283,7 @@ def show_change(cur_artist, cur_album, match): # Disambiguation. disambig = disambig_string(match.info) if disambig: - info.append(ui.colorize('text_highlight_minor', u'(%s)' % disambig)) + info.append(ui.colorize('text_highlight_minor', '(%s)' % disambig)) print_(' '.join(info)) # Tracks. @@ -297,16 +301,16 @@ def show_change(cur_artist, cur_album, match): if medium != track_info.medium or disctitle != track_info.disctitle: media = match.info.media or 'Media' if match.info.mediums > 1 and track_info.disctitle: - lhs = u'%s %s: %s' % (media, track_info.medium, - track_info.disctitle) + lhs = '{} {}: {}'.format(media, track_info.medium, + track_info.disctitle) elif match.info.mediums > 1: - lhs = u'%s %s' % (media, track_info.medium) + lhs = f'{media} {track_info.medium}' elif track_info.disctitle: - lhs = u'%s: %s' % (media, track_info.disctitle) + lhs = f'{media}: {track_info.disctitle}' else: lhs = None if lhs: - lines.append((lhs, u'', 0)) + lines.append((lhs, '', 0)) medium, disctitle = track_info.medium, track_info.disctitle # Titles. @@ -327,7 +331,7 @@ def show_change(cur_artist, cur_album, match): color = 'text_highlight_minor' else: color = 'text_highlight' - templ = ui.colorize(color, u' (#{0})') + templ = ui.colorize(color, ' (#{0})') lhs += templ.format(cur_track) rhs += templ.format(new_track) lhs_width += len(cur_track) + 4 @@ -338,7 +342,7 @@ def show_change(cur_artist, cur_album, match): config['ui']['length_diff_thresh'].as_number(): cur_length = ui.human_seconds_short(item.length) new_length = ui.human_seconds_short(track_info.length) - templ = ui.colorize('text_highlight', u' ({0})') + templ = ui.colorize('text_highlight', ' ({0})') lhs += templ.format(cur_length) rhs += templ.format(new_length) lhs_width += len(cur_length) + 3 @@ -349,9 +353,9 @@ def show_change(cur_artist, cur_album, match): rhs += ' %s' % penalties if lhs != rhs: - lines.append((u' * %s' % lhs, rhs, lhs_width)) + lines.append((' * %s' % lhs, rhs, lhs_width)) elif config['import']['detail']: - lines.append((u' * %s' % lhs, '', lhs_width)) + lines.append((' * %s' % lhs, '', lhs_width)) # Print each track in two columns, or across two lines. col_width = (ui.term_width() - len(''.join([' * ', ' -> ']))) // 2 @@ -361,29 +365,36 @@ def show_change(cur_artist, cur_album, match): if not rhs: print_(lhs) elif max_width > col_width: - print_(u'%s ->\n %s' % (lhs, rhs)) + print_(f'{lhs} ->\n {rhs}') else: pad = max_width - lhs_width - print_(u'%s%s -> %s' % (lhs, ' ' * pad, rhs)) + print_('{}{} -> {}'.format(lhs, ' ' * pad, rhs)) # Missing and unmatched tracks. if match.extra_tracks: - print_(u'Missing tracks ({0}/{1} - {2:.1%}):'.format( + print_('Missing tracks ({}/{} - {:.1%}):'.format( len(match.extra_tracks), len(match.info.tracks), len(match.extra_tracks) / len(match.info.tracks) )) + pad_width = max(len(track_info.title) for track_info in + match.extra_tracks) for track_info in match.extra_tracks: - line = u' ! %s (#%s)' % (track_info.title, format_index(track_info)) + line = ' ! {0: <{width}} (#{1: >2})'.format(track_info.title, + format_index(track_info), + width=pad_width) if track_info.length: - line += u' (%s)' % ui.human_seconds_short(track_info.length) + line += ' (%s)' % ui.human_seconds_short(track_info.length) print_(ui.colorize('text_warning', line)) if match.extra_items: - print_(u'Unmatched tracks ({0}):'.format(len(match.extra_items))) + print_('Unmatched tracks ({}):'.format(len(match.extra_items))) + pad_width = max(len(item.title) for item in match.extra_items) for item in match.extra_items: - line = u' ! %s (#%s)' % (item.title, format_index(item)) + line = ' ! {0: <{width}} (#{1: >2})'.format(item.title, + format_index(item), + width=pad_width) if item.length: - line += u' (%s)' % ui.human_seconds_short(item.length) + line += ' (%s)' % ui.human_seconds_short(item.length) print_(ui.colorize('text_warning', line)) @@ -398,22 +409,22 @@ def show_item_change(item, match): cur_artist, new_artist = ui.colordiff(cur_artist, new_artist) cur_title, new_title = ui.colordiff(cur_title, new_title) - print_(u"Correcting track tags from:") - print_(u" %s - %s" % (cur_artist, cur_title)) - print_(u"To:") - print_(u" %s - %s" % (new_artist, new_title)) + print_("Correcting track tags from:") + print_(f" {cur_artist} - {cur_title}") + print_("To:") + print_(f" {new_artist} - {new_title}") else: - print_(u"Tagging track: %s - %s" % (cur_artist, cur_title)) + print_(f"Tagging track: {cur_artist} - {cur_title}") # Data URL. if match.info.data_url: - print_(u'URL:\n %s' % match.info.data_url) + print_('URL:\n %s' % match.info.data_url) # Info line. info = [] # Similarity. - info.append(u'(Similarity: %s)' % dist_string(match.distance)) + info.append('(Similarity: %s)' % dist_string(match.distance)) # Penalties. penalties = penalty_string(match.distance) if penalties: @@ -421,7 +432,7 @@ def show_item_change(item, match): # Disambiguation. disambig = disambig_string(match.info) if disambig: - info.append(ui.colorize('text_highlight_minor', u'(%s)' % disambig)) + info.append(ui.colorize('text_highlight_minor', '(%s)' % disambig)) print_(' '.join(info)) @@ -435,7 +446,7 @@ def summarize_items(items, singleton): """ summary_parts = [] if not singleton: - summary_parts.append(u"{0} items".format(len(items))) + summary_parts.append("{} items".format(len(items))) format_counts = {} for item in items: @@ -449,26 +460,31 @@ def summarize_items(items, singleton): format_counts.items(), key=lambda fmt_and_count: (-fmt_and_count[1], fmt_and_count[0]) ): - summary_parts.append('{0} {1}'.format(fmt, count)) + summary_parts.append(f'{fmt} {count}') if items: average_bitrate = sum([item.bitrate for item in items]) / len(items) total_duration = sum([item.length for item in items]) total_filesize = sum([item.filesize for item in items]) - summary_parts.append(u'{0}kbps'.format(int(average_bitrate / 1000))) + summary_parts.append('{}kbps'.format(int(average_bitrate / 1000))) + if items[0].format == "FLAC": + sample_bits = '{}kHz/{} bit'.format( + round(int(items[0].samplerate) / 1000, 1), items[0].bitdepth) + summary_parts.append(sample_bits) summary_parts.append(ui.human_seconds_short(total_duration)) summary_parts.append(ui.human_bytes(total_filesize)) - return u', '.join(summary_parts) + return ', '.join(summary_parts) def _summary_judgment(rec): """Determines whether a decision should be made without even asking the user. This occurs in quiet mode and when an action is chosen for - NONE recommendations. Return an action or None if the user should be - queried. May also print to the console if a summary judgment is - made. + NONE recommendations. Return None if the user should be queried. + Otherwise, returns an action. May also print to the console if a + summary judgment is made. """ + if config['import']['quiet']: if rec == Recommendation.strong: return importer.action.APPLY @@ -477,21 +493,21 @@ def _summary_judgment(rec): 'skip': importer.action.SKIP, 'asis': importer.action.ASIS, }) - + elif config['import']['timid']: + return None elif rec == Recommendation.none: action = config['import']['none_rec_action'].as_choice({ 'skip': importer.action.SKIP, 'asis': importer.action.ASIS, 'ask': None, }) - else: return None if action == importer.action.SKIP: - print_(u'Skipping.') + print_('Skipping.') elif action == importer.action.ASIS: - print_(u'Importing as-is.') + print_('Importing as-is.') return action @@ -526,12 +542,12 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, # Zero candidates. if not candidates: if singleton: - print_(u"No matching recordings found.") + print_("No matching recordings found.") else: - print_(u"No matching release found for {0} tracks." + print_("No matching release found for {} tracks." .format(itemcount)) - print_(u'For help, see: ' - u'http://beets.readthedocs.org/en/latest/faq.html#nomatch') + print_('For help, see: ' + 'https://beets.readthedocs.org/en/latest/faq.html#nomatch') sel = ui.input_options(choice_opts) if sel in choice_actions: return choice_actions[sel] @@ -550,22 +566,22 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, if not bypass_candidates: # Display list of candidates. - print_(u'Finding tags for {0} "{1} - {2}".'.format( - u'track' if singleton else u'album', + print_('Finding tags for {} "{} - {}".'.format( + 'track' if singleton else 'album', item.artist if singleton else cur_artist, item.title if singleton else cur_album, )) - print_(u'Candidates:') + print_('Candidates:') for i, match in enumerate(candidates): # Index, metadata, and distance. line = [ - u'{0}.'.format(i + 1), - u'{0} - {1}'.format( + '{}.'.format(i + 1), + '{} - {}'.format( match.info.artist, match.info.title if singleton else match.info.album, ), - u'({0})'.format(dist_string(match.distance)), + '({})'.format(dist_string(match.distance)), ] # Penalties. @@ -577,14 +593,14 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, disambig = disambig_string(match.info) if disambig: line.append(ui.colorize('text_highlight_minor', - u'(%s)' % disambig)) + '(%s)' % disambig)) - print_(u' '.join(line)) + print_(' '.join(line)) # Ask the user for a choice. sel = ui.input_options(choice_opts, numrange=(1, len(candidates))) - if sel == u'm': + if sel == 'm': pass elif sel in choice_actions: return choice_actions[sel] @@ -608,19 +624,19 @@ def choose_candidate(candidates, singleton, rec, cur_artist=None, # Ask for confirmation. default = config['import']['default_action'].as_choice({ - u'apply': u'a', - u'skip': u's', - u'asis': u'u', - u'none': None, + 'apply': 'a', + 'skip': 's', + 'asis': 'u', + 'none': None, }) if default is None: require = True # Bell ring when user interaction is needed. if config['import']['bell']: - ui.print_(u'\a', end=u'') - sel = ui.input_options((u'Apply', u'More candidates') + choice_opts, + ui.print_('\a', end='') + sel = ui.input_options(('Apply', 'More candidates') + choice_opts, require=require, default=default) - if sel == u'a': + if sel == 'a': return match elif sel in choice_actions: return choice_actions[sel] @@ -632,8 +648,8 @@ def manual_search(session, task): Input either an artist and album (for full albums) or artist and track name (for singletons) for manual search. """ - artist = input_(u'Artist:').strip() - name = input_(u'Album:' if task.is_album else u'Track:').strip() + artist = input_('Artist:').strip() + name = input_('Album:' if task.is_album else 'Track:').strip() if task.is_album: _, _, prop = autotag.tag_album( @@ -649,8 +665,8 @@ def manual_id(session, task): Input an ID, either for an album ("release") or a track ("recording"). """ - prompt = u'Enter {0} ID:'.format(u'release' if task.is_album - else u'recording') + prompt = 'Enter {} ID:'.format('release' if task.is_album + else 'recording') search_id = input_(prompt).strip() if task.is_album: @@ -671,6 +687,7 @@ def abort_action(session, task): class TerminalImportSession(importer.ImportSession): """An import session that runs in a terminal. """ + def choose_match(self, task): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an @@ -678,8 +695,21 @@ class TerminalImportSession(importer.ImportSession): """ # Show what we're tagging. print_() - print_(displayable_path(task.paths, u'\n') + - u' ({0} items)'.format(len(task.items))) + print_(displayable_path(task.paths, '\n') + + ' ({} items)'.format(len(task.items))) + + # Let plugins display info or prompt the user before we go through the + # process of selecting candidate. + results = plugins.send('import_task_before_choice', + session=self, task=task) + actions = [action for action in results if action] + + if len(actions) == 1: + return actions[0] + elif len(actions) > 1: + raise plugins.PluginConflictException( + 'Only one handler for `import_task_before_choice` may return ' + 'an action.') # Take immediate action if appropriate. action = _summary_judgment(task.rec) @@ -768,48 +798,48 @@ class TerminalImportSession(importer.ImportSession): """Decide what to do when a new album or item seems similar to one that's already in the library. """ - log.warning(u"This {0} is already in the library!", - (u"album" if task.is_album else u"item")) + log.warning("This {0} is already in the library!", + ("album" if task.is_album else "item")) if config['import']['quiet']: # In quiet mode, don't prompt -- just skip. - log.info(u'Skipping.') - sel = u's' + log.info('Skipping.') + sel = 's' else: # Print some detail about the existing and new items so the # user can make an informed decision. for duplicate in found_duplicates: - print_(u"Old: " + summarize_items( + print_("Old: " + summarize_items( list(duplicate.items()) if task.is_album else [duplicate], not task.is_album, )) - print_(u"New: " + summarize_items( + print_("New: " + summarize_items( task.imported_items(), not task.is_album, )) sel = ui.input_options( - (u'Skip new', u'Keep both', u'Remove old', u'Merge all') + ('Skip new', 'Keep all', 'Remove old', 'Merge all') ) - if sel == u's': + if sel == 's': # Skip new. task.set_choice(importer.action.SKIP) - elif sel == u'k': + elif sel == 'k': # Keep both. Do nothing; leave the choice intact. pass - elif sel == u'r': + elif sel == 'r': # Remove old. task.should_remove_duplicates = True - elif sel == u'm': + elif sel == 'm': task.should_merge_duplicates = True else: assert False def should_resume(self, path): - return ui.input_yn(u"Import of the directory:\n{0}\n" - u"was interrupted. Resume (Y/n)?" + return ui.input_yn("Import of the directory:\n{}\n" + "was interrupted. Resume (Y/n)?" .format(displayable_path(path))) def _get_choices(self, task): @@ -830,22 +860,22 @@ class TerminalImportSession(importer.ImportSession): """ # Standard, built-in choices. choices = [ - PromptChoice(u's', u'Skip', + PromptChoice('s', 'Skip', lambda s, t: importer.action.SKIP), - PromptChoice(u'u', u'Use as-is', + PromptChoice('u', 'Use as-is', lambda s, t: importer.action.ASIS) ] if task.is_album: choices += [ - PromptChoice(u't', u'as Tracks', + PromptChoice('t', 'as Tracks', lambda s, t: importer.action.TRACKS), - PromptChoice(u'g', u'Group albums', + PromptChoice('g', 'Group albums', lambda s, t: importer.action.ALBUMS), ] choices += [ - PromptChoice(u'e', u'Enter search', manual_search), - PromptChoice(u'i', u'enter Id', manual_id), - PromptChoice(u'b', u'aBort', abort_action), + PromptChoice('e', 'Enter search', manual_search), + PromptChoice('i', 'enter Id', manual_id), + PromptChoice('b', 'aBort', abort_action), ] # Send the before_choose_candidate event and flatten list. @@ -855,7 +885,7 @@ class TerminalImportSession(importer.ImportSession): # Add a "dummy" choice for the other baked-in option, for # duplicate checking. all_choices = [ - PromptChoice(u'a', u'Apply', None), + PromptChoice('a', 'Apply', None), ] + choices + extra_choices # Check for conflicts. @@ -868,8 +898,8 @@ class TerminalImportSession(importer.ImportSession): # Keep the first of the choices, removing the rest. dup_choices = [c for c in all_choices if c.short == short] for c in dup_choices[1:]: - log.warning(u"Prompt choice '{0}' removed due to conflict " - u"with '{1}' (short letter: '{2}')", + log.warning("Prompt choice '{0}' removed due to conflict " + "with '{1}' (short letter: '{2}')", c.long, dup_choices[0].long, c.short) extra_choices.remove(c) @@ -886,21 +916,21 @@ def import_files(lib, paths, query): # Check the user-specified directories. for path in paths: if not os.path.exists(syspath(normpath(path))): - raise ui.UserError(u'no such file or directory: {0}'.format( + raise ui.UserError('no such file or directory: {}'.format( displayable_path(path))) # Check parameter consistency. if config['import']['quiet'] and config['import']['timid']: - raise ui.UserError(u"can't be both quiet and timid") + raise ui.UserError("can't be both quiet and timid") # Open the log. if config['import']['log'].get() is not None: logpath = syspath(config['import']['log'].as_filename()) try: loghandler = logging.FileHandler(logpath) - except IOError: - raise ui.UserError(u"could not open log file for writing: " - u"{0}".format(displayable_path(logpath))) + except OSError: + raise ui.UserError("could not open log file for writing: " + "{}".format(displayable_path(logpath))) else: loghandler = None @@ -931,111 +961,111 @@ def import_func(lib, opts, args): query = None paths = args if not paths: - raise ui.UserError(u'no path specified') + raise ui.UserError('no path specified') - # On Python 2, we get filenames as raw bytes, which is what we - # need. On Python 3, we need to undo the "helpful" conversion to - # Unicode strings to get the real bytestring filename. - if not six.PY2: - paths = [p.encode(util.arg_encoding(), 'surrogateescape') - for p in paths] + # On Python 2, we used to get filenames as raw bytes, which is + # what we need. On Python 3, we need to undo the "helpful" + # conversion to Unicode strings to get the real bytestring + # filename. + paths = [p.encode(util.arg_encoding(), 'surrogateescape') + for p in paths] import_files(lib, paths, query) import_cmd = ui.Subcommand( - u'import', help=u'import new music', aliases=(u'imp', u'im') + 'import', help='import new music', aliases=('imp', 'im') ) import_cmd.parser.add_option( - u'-c', u'--copy', action='store_true', default=None, - help=u"copy tracks into library directory (default)" + '-c', '--copy', action='store_true', default=None, + help="copy tracks into library directory (default)" ) import_cmd.parser.add_option( - u'-C', u'--nocopy', action='store_false', dest='copy', - help=u"don't copy tracks (opposite of -c)" + '-C', '--nocopy', action='store_false', dest='copy', + help="don't copy tracks (opposite of -c)" ) import_cmd.parser.add_option( - u'-m', u'--move', action='store_true', dest='move', - help=u"move tracks into the library (overrides -c)" + '-m', '--move', action='store_true', dest='move', + help="move tracks into the library (overrides -c)" ) import_cmd.parser.add_option( - u'-w', u'--write', action='store_true', default=None, - help=u"write new metadata to files' tags (default)" + '-w', '--write', action='store_true', default=None, + help="write new metadata to files' tags (default)" ) import_cmd.parser.add_option( - u'-W', u'--nowrite', action='store_false', dest='write', - help=u"don't write metadata (opposite of -w)" + '-W', '--nowrite', action='store_false', dest='write', + help="don't write metadata (opposite of -w)" ) import_cmd.parser.add_option( - u'-a', u'--autotag', action='store_true', dest='autotag', - help=u"infer tags for imported files (default)" + '-a', '--autotag', action='store_true', dest='autotag', + help="infer tags for imported files (default)" ) import_cmd.parser.add_option( - u'-A', u'--noautotag', action='store_false', dest='autotag', - help=u"don't infer tags for imported files (opposite of -a)" + '-A', '--noautotag', action='store_false', dest='autotag', + help="don't infer tags for imported files (opposite of -a)" ) import_cmd.parser.add_option( - u'-p', u'--resume', action='store_true', default=None, - help=u"resume importing if interrupted" + '-p', '--resume', action='store_true', default=None, + help="resume importing if interrupted" ) import_cmd.parser.add_option( - u'-P', u'--noresume', action='store_false', dest='resume', - help=u"do not try to resume importing" + '-P', '--noresume', action='store_false', dest='resume', + help="do not try to resume importing" ) import_cmd.parser.add_option( - u'-q', u'--quiet', action='store_true', dest='quiet', - help=u"never prompt for input: skip albums instead" + '-q', '--quiet', action='store_true', dest='quiet', + help="never prompt for input: skip albums instead" ) import_cmd.parser.add_option( - u'-l', u'--log', dest='log', - help=u'file to log untaggable albums for later review' + '-l', '--log', dest='log', + help='file to log untaggable albums for later review' ) import_cmd.parser.add_option( - u'-s', u'--singletons', action='store_true', - help=u'import individual tracks instead of full albums' + '-s', '--singletons', action='store_true', + help='import individual tracks instead of full albums' ) import_cmd.parser.add_option( - u'-t', u'--timid', dest='timid', action='store_true', - help=u'always confirm all actions' + '-t', '--timid', dest='timid', action='store_true', + help='always confirm all actions' ) import_cmd.parser.add_option( - u'-L', u'--library', dest='library', action='store_true', - help=u'retag items matching a query' + '-L', '--library', dest='library', action='store_true', + help='retag items matching a query' ) import_cmd.parser.add_option( - u'-i', u'--incremental', dest='incremental', action='store_true', - help=u'skip already-imported directories' + '-i', '--incremental', dest='incremental', action='store_true', + help='skip already-imported directories' ) import_cmd.parser.add_option( - u'-I', u'--noincremental', dest='incremental', action='store_false', - help=u'do not skip already-imported directories' + '-I', '--noincremental', dest='incremental', action='store_false', + help='do not skip already-imported directories' ) import_cmd.parser.add_option( - u'--from-scratch', dest='from_scratch', action='store_true', - help=u'erase existing metadata before applying new metadata' + '--from-scratch', dest='from_scratch', action='store_true', + help='erase existing metadata before applying new metadata' ) import_cmd.parser.add_option( - u'--flat', dest='flat', action='store_true', - help=u'import an entire tree as a single album' + '--flat', dest='flat', action='store_true', + help='import an entire tree as a single album' ) import_cmd.parser.add_option( - u'-g', u'--group-albums', dest='group_albums', action='store_true', - help=u'group tracks in a folder into separate albums' + '-g', '--group-albums', dest='group_albums', action='store_true', + help='group tracks in a folder into separate albums' ) import_cmd.parser.add_option( - u'--pretend', dest='pretend', action='store_true', - help=u'just print the files to import' + '--pretend', dest='pretend', action='store_true', + help='just print the files to import' ) import_cmd.parser.add_option( - u'-S', u'--search-id', dest='search_ids', action='append', + '-S', '--search-id', dest='search_ids', action='append', metavar='ID', - help=u'restrict matching to a specific metadata backend ID' + help='restrict matching to a specific metadata backend ID' ) import_cmd.parser.add_option( - u'--set', dest='set_fields', action='callback', + '--set', dest='set_fields', action='callback', callback=_store_dict, metavar='FIELD=VALUE', - help=u'set the given fields to the supplied values' + help='set the given fields to the supplied values' ) import_cmd.func = import_func default_commands.append(import_cmd) @@ -1043,7 +1073,7 @@ default_commands.append(import_cmd) # list: Query and show library contents. -def list_items(lib, query, album, fmt=u''): +def list_items(lib, query, album, fmt=''): """Print out items in lib matching query. If album, then search for albums instead of single items. """ @@ -1059,9 +1089,9 @@ def list_func(lib, opts, args): list_items(lib, decargs(args), opts.album) -list_cmd = ui.Subcommand(u'list', help=u'query the library', aliases=(u'ls',)) -list_cmd.parser.usage += u"\n" \ - u'Example: %prog -f \'$album: $title\' artist:beatles' +list_cmd = ui.Subcommand('list', help='query the library', aliases=('ls',)) +list_cmd.parser.usage += "\n" \ + 'Example: %prog -f \'$album: $title\' artist:beatles' list_cmd.parser.add_all_common_options() list_cmd.func = list_func default_commands.append(list_cmd) @@ -1089,7 +1119,7 @@ def update_items(lib, query, album, move, pretend, fields): # Item deleted? if not os.path.exists(syspath(item.path)): ui.print_(format(item)) - ui.print_(ui.colorize('text_error', u' deleted')) + ui.print_(ui.colorize('text_error', ' deleted')) if not pretend: item.remove(True) affected_albums.add(item.album_id) @@ -1097,7 +1127,7 @@ def update_items(lib, query, album, move, pretend, fields): # Did the item change since last checked? if item.current_mtime() <= item.mtime: - log.debug(u'skipping {0} because mtime is up to date ({1})', + log.debug('skipping {0} because mtime is up to date ({1})', displayable_path(item.path), item.mtime) continue @@ -1105,7 +1135,7 @@ def update_items(lib, query, album, move, pretend, fields): try: item.read() except library.ReadError as exc: - log.error(u'error reading {0}: {1}', + log.error('error reading {0}: {1}', displayable_path(item.path), exc) continue @@ -1116,7 +1146,7 @@ def update_items(lib, query, album, move, pretend, fields): old_item = lib.get_item(item.id) if old_item.albumartist == old_item.artist == item.artist: item.albumartist = old_item.albumartist - item._dirty.discard(u'albumartist') + item._dirty.discard('albumartist') # Check for and display changes. changed = ui.show_model_changes( @@ -1149,7 +1179,7 @@ def update_items(lib, query, album, move, pretend, fields): continue album = lib.get_album(album_id) if not album: # Empty albums have already been removed. - log.debug(u'emptied album {0}', album_id) + log.debug('emptied album {0}', album_id) continue first_item = album.items().get() @@ -1160,42 +1190,48 @@ def update_items(lib, query, album, move, pretend, fields): # Move album art (and any inconsistent items). if move and lib.directory in ancestry(first_item.path): - log.debug(u'moving album {0}', album_id) + log.debug('moving album {0}', album_id) # Manually moving and storing the album. items = list(album.items()) for item in items: - item.move(store=False) + item.move(store=False, with_album=False) item.store(fields=fields) album.move(store=False) album.store(fields=fields) def update_func(lib, opts, args): + # Verify that the library folder exists to prevent accidental wipes. + if not os.path.isdir(lib.directory): + ui.print_("Library path is unavailable or does not exist.") + ui.print_(lib.directory) + if not ui.input_yn("Are you sure you want to continue (y/n)?", True): + return update_items(lib, decargs(args), opts.album, ui.should_move(opts.move), opts.pretend, opts.fields) update_cmd = ui.Subcommand( - u'update', help=u'update the library', aliases=(u'upd', u'up',) + 'update', help='update the library', aliases=('upd', 'up',) ) update_cmd.parser.add_album_option() update_cmd.parser.add_format_option() update_cmd.parser.add_option( - u'-m', u'--move', action='store_true', dest='move', - help=u"move files in the library directory" + '-m', '--move', action='store_true', dest='move', + help="move files in the library directory" ) update_cmd.parser.add_option( - u'-M', u'--nomove', action='store_false', dest='move', - help=u"don't move files in library" + '-M', '--nomove', action='store_false', dest='move', + help="don't move files in library" ) update_cmd.parser.add_option( - u'-p', u'--pretend', action='store_true', - help=u"show all changes but do nothing" + '-p', '--pretend', action='store_true', + help="show all changes but do nothing" ) update_cmd.parser.add_option( - u'-F', u'--field', default=None, action='append', dest='fields', - help=u'list of fields to update' + '-F', '--field', default=None, action='append', dest='fields', + help='list of fields to update' ) update_cmd.func = update_func default_commands.append(update_cmd) @@ -1209,31 +1245,53 @@ def remove_items(lib, query, album, delete, force): """ # Get the matching items. items, albums = _do_query(lib, query, album) + objs = albums if album else items # Confirm file removal if not forcing removal. if not force: # Prepare confirmation with user. - print_() + album_str = " in {} album{}".format( + len(albums), 's' if len(albums) > 1 else '' + ) if album else "" + if delete: - fmt = u'$path - $title' - prompt = u'Really DELETE %i file%s (y/n)?' % \ - (len(items), 's' if len(items) > 1 else '') + fmt = '$path - $title' + prompt = 'Really DELETE' + prompt_all = 'Really DELETE {} file{}{}'.format( + len(items), 's' if len(items) > 1 else '', album_str + ) else: - fmt = u'' - prompt = u'Really remove %i item%s from the library (y/n)?' % \ - (len(items), 's' if len(items) > 1 else '') + fmt = '' + prompt = 'Really remove from the library?' + prompt_all = 'Really remove {} item{}{} from the library?'.format( + len(items), 's' if len(items) > 1 else '', album_str + ) + + # Helpers for printing affected items + def fmt_track(t): + ui.print_(format(t, fmt)) + + def fmt_album(a): + ui.print_() + for i in a.items(): + fmt_track(i) + + fmt_obj = fmt_album if album else fmt_track # Show all the items. - for item in items: - ui.print_(format(item, fmt)) + for o in objs: + fmt_obj(o) # Confirm with user. - if not ui.input_yn(prompt, True): - return + objs = ui.input_select_objects(prompt, objs, fmt_obj, + prompt_all=prompt_all) + + if not objs: + return # Remove (and possibly delete) items. with lib.transaction(): - for obj in (albums if album else items): + for obj in objs: obj.remove(delete) @@ -1242,15 +1300,15 @@ def remove_func(lib, opts, args): remove_cmd = ui.Subcommand( - u'remove', help=u'remove matching items from the library', aliases=(u'rm',) + 'remove', help='remove matching items from the library', aliases=('rm',) ) remove_cmd.parser.add_option( - u"-d", u"--delete", action="store_true", - help=u"also remove files from disk" + "-d", "--delete", action="store_true", + help="also remove files from disk" ) remove_cmd.parser.add_option( - u"-f", u"--force", action="store_true", - help=u"do not ask when removing items" + "-f", "--force", action="store_true", + help="do not ask when removing items" ) remove_cmd.parser.add_album_option() remove_cmd.func = remove_func @@ -1275,7 +1333,7 @@ def show_stats(lib, query, exact): try: total_size += os.path.getsize(syspath(item.path)) except OSError as exc: - log.info(u'could not get size of {}: {}', item.path, exc) + log.info('could not get size of {}: {}', item.path, exc) else: total_size += int(item.length * item.bitrate / 8) total_time += item.length @@ -1285,20 +1343,20 @@ def show_stats(lib, query, exact): if item.album_id: albums.add(item.album_id) - size_str = u'' + ui.human_bytes(total_size) + size_str = '' + ui.human_bytes(total_size) if exact: - size_str += u' ({0} bytes)'.format(total_size) + size_str += f' ({total_size} bytes)' - print_(u"""Tracks: {0} -Total time: {1}{2} -{3}: {4} -Artists: {5} -Albums: {6} -Album artists: {7}""".format( + print_("""Tracks: {} +Total time: {}{} +{}: {} +Artists: {} +Albums: {} +Album artists: {}""".format( total_items, ui.human_seconds(total_time), - u' ({0:.2f} seconds)'.format(total_time) if exact else '', - u'Total size' if exact else u'Approximate total size', + f' ({total_time:.2f} seconds)' if exact else '', + 'Total size' if exact else 'Approximate total size', size_str, len(artists), len(albums), @@ -1311,11 +1369,11 @@ def stats_func(lib, opts, args): stats_cmd = ui.Subcommand( - u'stats', help=u'show statistics about the library or a query' + 'stats', help='show statistics about the library or a query' ) stats_cmd.parser.add_option( - u'-e', u'--exact', action='store_true', - help=u'exact size and time' + '-e', '--exact', action='store_true', + help='exact size and time' ) stats_cmd.func = stats_func default_commands.append(stats_cmd) @@ -1324,18 +1382,18 @@ default_commands.append(stats_cmd) # version: Show current beets version. def show_version(lib, opts, args): - print_(u'beets version %s' % beets.__version__) - print_(u'Python version {}'.format(python_version())) + print_('beets version %s' % beets.__version__) + print_(f'Python version {python_version()}') # Show plugins. names = sorted(p.name for p in plugins.find_plugins()) if names: - print_(u'plugins:', ', '.join(names)) + print_('plugins:', ', '.join(names)) else: - print_(u'no plugins loaded') + print_('no plugins loaded') version_cmd = ui.Subcommand( - u'version', help=u'output version information' + 'version', help='output version information' ) version_cmd.func = show_version default_commands.append(version_cmd) @@ -1362,31 +1420,31 @@ def modify_items(lib, mods, dels, query, write, move, album, confirm): # Apply changes *temporarily*, preview them, and collect modified # objects. - print_(u'Modifying {0} {1}s.' - .format(len(objs), u'album' if album else u'item')) - changed = set() + print_('Modifying {} {}s.' + .format(len(objs), 'album' if album else 'item')) + changed = [] for obj in objs: - if print_and_modify(obj, mods, dels): - changed.add(obj) + if print_and_modify(obj, mods, dels) and obj not in changed: + changed.append(obj) # Still something to do? if not changed: - print_(u'No changes to make.') + print_('No changes to make.') return # Confirm action. if confirm: if write and move: - extra = u', move and write tags' + extra = ', move and write tags' elif write: - extra = u' and write tags' + extra = ' and write tags' elif move: - extra = u' and move' + extra = ' and move' else: - extra = u'' + extra = '' changed = ui.input_select_objects( - u'Really modify%s' % extra, changed, + 'Really modify%s' % extra, changed, lambda o: print_and_modify(o, mods, dels) ) @@ -1434,35 +1492,35 @@ def modify_parse_args(args): def modify_func(lib, opts, args): query, mods, dels = modify_parse_args(decargs(args)) if not mods and not dels: - raise ui.UserError(u'no modifications specified') + raise ui.UserError('no modifications specified') modify_items(lib, mods, dels, query, ui.should_write(opts.write), ui.should_move(opts.move), opts.album, not opts.yes) modify_cmd = ui.Subcommand( - u'modify', help=u'change metadata fields', aliases=(u'mod',) + 'modify', help='change metadata fields', aliases=('mod',) ) modify_cmd.parser.add_option( - u'-m', u'--move', action='store_true', dest='move', - help=u"move files in the library directory" + '-m', '--move', action='store_true', dest='move', + help="move files in the library directory" ) modify_cmd.parser.add_option( - u'-M', u'--nomove', action='store_false', dest='move', - help=u"don't move files in library" + '-M', '--nomove', action='store_false', dest='move', + help="don't move files in library" ) modify_cmd.parser.add_option( - u'-w', u'--write', action='store_true', default=None, - help=u"write new metadata to files' tags (default)" + '-w', '--write', action='store_true', default=None, + help="write new metadata to files' tags (default)" ) modify_cmd.parser.add_option( - u'-W', u'--nowrite', action='store_false', dest='write', - help=u"don't write metadata (opposite of -w)" + '-W', '--nowrite', action='store_false', dest='write', + help="don't write metadata (opposite of -w)" ) modify_cmd.parser.add_album_option() modify_cmd.parser.add_format_option(target='item') modify_cmd.parser.add_option( - u'-y', u'--yes', action='store_true', - help=u'skip confirmation' + '-y', '--yes', action='store_true', + help='skip confirmation' ) modify_cmd.func = modify_func default_commands.append(modify_cmd) @@ -1478,18 +1536,28 @@ def move_items(lib, dest, query, copy, album, pretend, confirm=False, """ items, albums = _do_query(lib, query, album, False) objs = albums if album else items + num_objs = len(objs) # Filter out files that don't need to be moved. - isitemmoved = lambda item: item.path != item.destination(basedir=dest) - isalbummoved = lambda album: any(isitemmoved(i) for i in album.items()) + def isitemmoved(item): + return item.path != item.destination(basedir=dest) + + def isalbummoved(album): + return any(isitemmoved(i) for i in album.items()) + objs = [o for o in objs if (isalbummoved if album else isitemmoved)(o)] + num_unmoved = num_objs - len(objs) + # Report unmoved files that match the query. + unmoved_msg = '' + if num_unmoved > 0: + unmoved_msg = f' ({num_unmoved} already in place)' copy = copy or export # Exporting always copies. - action = u'Copying' if copy else u'Moving' - act = u'copy' if copy else u'move' - entity = u'album' if album else u'item' - log.info(u'{0} {1} {2}{3}.', action, len(objs), entity, - u's' if len(objs) != 1 else u'') + action = 'Copying' if copy else 'Moving' + act = 'copy' if copy else 'move' + entity = 'album' if album else 'item' + log.info('{0} {1} {2}{3}{4}.', action, len(objs), entity, + 's' if len(objs) != 1 else '', unmoved_msg) if not objs: return @@ -1503,12 +1571,12 @@ def move_items(lib, dest, query, copy, album, pretend, confirm=False, else: if confirm: objs = ui.input_select_objects( - u'Really %s' % act, objs, + 'Really %s' % act, objs, lambda o: show_path_changes( [(o.path, o.destination(basedir=dest))])) for obj in objs: - log.debug(u'moving: {0}', util.displayable_path(obj.path)) + log.debug('moving: {0}', util.displayable_path(obj.path)) if export: # Copy without affecting the database. @@ -1527,34 +1595,34 @@ def move_func(lib, opts, args): if dest is not None: dest = normpath(dest) if not os.path.isdir(dest): - raise ui.UserError(u'no such directory: %s' % dest) + raise ui.UserError('no such directory: %s' % dest) move_items(lib, dest, decargs(args), opts.copy, opts.album, opts.pretend, opts.timid, opts.export) move_cmd = ui.Subcommand( - u'move', help=u'move or copy items', aliases=(u'mv',) + 'move', help='move or copy items', aliases=('mv',) ) move_cmd.parser.add_option( - u'-d', u'--dest', metavar='DIR', dest='dest', - help=u'destination directory' + '-d', '--dest', metavar='DIR', dest='dest', + help='destination directory' ) move_cmd.parser.add_option( - u'-c', u'--copy', default=False, action='store_true', - help=u'copy instead of moving' + '-c', '--copy', default=False, action='store_true', + help='copy instead of moving' ) move_cmd.parser.add_option( - u'-p', u'--pretend', default=False, action='store_true', - help=u'show how files would be moved, but don\'t touch anything' + '-p', '--pretend', default=False, action='store_true', + help='show how files would be moved, but don\'t touch anything' ) move_cmd.parser.add_option( - u'-t', u'--timid', dest='timid', action='store_true', - help=u'always confirm all actions' + '-t', '--timid', dest='timid', action='store_true', + help='always confirm all actions' ) move_cmd.parser.add_option( - u'-e', u'--export', default=False, action='store_true', - help=u'copy without changing the database path' + '-e', '--export', default=False, action='store_true', + help='copy without changing the database path' ) move_cmd.parser.add_album_option() move_cmd.func = move_func @@ -1572,14 +1640,14 @@ def write_items(lib, query, pretend, force): for item in items: # Item deleted? if not os.path.exists(syspath(item.path)): - log.info(u'missing file: {0}', util.displayable_path(item.path)) + log.info('missing file: {0}', util.displayable_path(item.path)) continue # Get an Item object reflecting the "clean" (on-disk) state. try: clean_item = library.Item.from_path(item.path) except library.ReadError as exc: - log.error(u'error reading {0}: {1}', + log.error('error reading {0}: {1}', displayable_path(item.path), exc) continue @@ -1596,14 +1664,14 @@ def write_func(lib, opts, args): write_items(lib, decargs(args), opts.pretend, opts.force) -write_cmd = ui.Subcommand(u'write', help=u'write tag information to files') +write_cmd = ui.Subcommand('write', help='write tag information to files') write_cmd.parser.add_option( - u'-p', u'--pretend', action='store_true', - help=u"show all changes but do nothing" + '-p', '--pretend', action='store_true', + help="show all changes but do nothing" ) write_cmd.parser.add_option( - u'-f', u'--force', action='store_true', - help=u"write tags even if the existing tags match the database" + '-f', '--force', action='store_true', + help="write tags even if the existing tags match the database" ) write_cmd.func = write_func default_commands.append(write_cmd) @@ -1640,7 +1708,10 @@ def config_func(lib, opts, args): # Dump configuration. else: config_out = config.dump(full=opts.defaults, redact=opts.redact) - print_(util.text_string(config_out)) + if config_out.strip() != '{}': + print_(util.text_string(config_out)) + else: + print("Empty configuration") def config_edit(): @@ -1654,29 +1725,30 @@ def config_edit(): open(path, 'w+').close() util.interactive_open([path], editor) except OSError as exc: - message = u"Could not edit configuration: {0}".format(exc) + message = f"Could not edit configuration: {exc}" if not editor: - message += u". Please set the EDITOR environment variable" + message += ". Please set the EDITOR environment variable" raise ui.UserError(message) -config_cmd = ui.Subcommand(u'config', - help=u'show or edit the user configuration') + +config_cmd = ui.Subcommand('config', + help='show or edit the user configuration') config_cmd.parser.add_option( - u'-p', u'--paths', action='store_true', - help=u'show files that configuration was loaded from' + '-p', '--paths', action='store_true', + help='show files that configuration was loaded from' ) config_cmd.parser.add_option( - u'-e', u'--edit', action='store_true', - help=u'edit user configuration with $EDITOR' + '-e', '--edit', action='store_true', + help='edit user configuration with $EDITOR' ) config_cmd.parser.add_option( - u'-d', u'--defaults', action='store_true', - help=u'include the default configuration' + '-d', '--defaults', action='store_true', + help='include the default configuration' ) config_cmd.parser.add_option( - u'-c', u'--clear', action='store_false', + '-c', '--clear', action='store_false', dest='redact', default=True, - help=u'do not redact sensitive fields' + help='do not redact sensitive fields' ) config_cmd.func = config_func default_commands.append(config_cmd) @@ -1686,19 +1758,20 @@ default_commands.append(config_cmd) def print_completion(*args): for line in completion_script(default_commands + plugins.commands()): - print_(line, end=u'') + print_(line, end='') if not any(map(os.path.isfile, BASH_COMPLETION_PATHS)): - log.warning(u'Warning: Unable to find the bash-completion package. ' - u'Command line completion might not work.') + log.warning('Warning: Unable to find the bash-completion package. ' + 'Command line completion might not work.') + BASH_COMPLETION_PATHS = map(syspath, [ - u'/etc/bash_completion', - u'/usr/share/bash-completion/bash_completion', - u'/usr/local/share/bash-completion/bash_completion', + '/etc/bash_completion', + '/usr/share/bash-completion/bash_completion', + '/usr/local/share/bash-completion/bash_completion', # SmartOS - u'/opt/local/share/bash-completion/bash_completion', + '/opt/local/share/bash-completion/bash_completion', # Homebrew (before bash-completion2) - u'/usr/local/etc/bash_completion', + '/usr/local/etc/bash_completion', ]) @@ -1708,8 +1781,8 @@ def completion_script(commands): ``commands`` is alist of ``ui.Subcommand`` instances to generate completion data for. """ - base_script = os.path.join(_package_path('beets.ui'), 'completion_base.sh') - with open(base_script, 'r') as base_script: + base_script = os.path.join(os.path.dirname(__file__), 'completion_base.sh') + with open(base_script) as base_script: yield util.text_string(base_script.read()) options = {} @@ -1725,12 +1798,12 @@ def completion_script(commands): if re.match(r'^\w+$', alias): aliases[alias] = name - options[name] = {u'flags': [], u'opts': []} + options[name] = {'flags': [], 'opts': []} for opts in cmd.parser._get_all_options()[1:]: if opts.action in ('store_true', 'store_false'): - option_type = u'flags' + option_type = 'flags' else: - option_type = u'opts' + option_type = 'opts' options[name][option_type].extend( opts._short_opts + opts._long_opts @@ -1738,31 +1811,31 @@ def completion_script(commands): # Add global options options['_global'] = { - u'flags': [u'-v', u'--verbose'], - u'opts': - u'-l --library -c --config -d --directory -h --help'.split(u' ') + 'flags': ['-v', '--verbose'], + 'opts': + '-l --library -c --config -d --directory -h --help'.split(' ') } # Add flags common to all commands options['_common'] = { - u'flags': [u'-h', u'--help'] + 'flags': ['-h', '--help'] } # Start generating the script - yield u"_beet() {\n" + yield "_beet() {\n" # Command names - yield u" local commands='%s'\n" % ' '.join(command_names) - yield u"\n" + yield " local commands='%s'\n" % ' '.join(command_names) + yield "\n" # Command aliases - yield u" local aliases='%s'\n" % ' '.join(aliases.keys()) + yield " local aliases='%s'\n" % ' '.join(aliases.keys()) for alias, cmd in aliases.items(): - yield u" local alias__%s=%s\n" % (alias.replace('-', '_'), cmd) - yield u'\n' + yield " local alias__{}={}\n".format(alias.replace('-', '_'), cmd) + yield '\n' # Fields - yield u" fields='%s'\n" % ' '.join( + yield " fields='%s'\n" % ' '.join( set( list(library.Item._fields.keys()) + list(library.Album._fields.keys()) @@ -1773,17 +1846,17 @@ def completion_script(commands): for cmd, opts in options.items(): for option_type, option_list in opts.items(): if option_list: - option_list = u' '.join(option_list) - yield u" local %s__%s='%s'\n" % ( + option_list = ' '.join(option_list) + yield " local {}__{}='{}'\n".format( option_type, cmd.replace('-', '_'), option_list) - yield u' _beet_dispatch\n' - yield u'}\n' + yield ' _beet_dispatch\n' + yield '}\n' completion_cmd = ui.Subcommand( 'completion', - help=u'print shell script that provides command line completion' + help='print shell script that provides command line completion' ) completion_cmd.func = print_completion completion_cmd.hide = True diff --git a/libs/common/beets/util/__init__.py b/libs/common/beets/util/__init__.py index 69870edf..d58bb28e 100644 --- a/libs/common/beets/util/__init__.py +++ b/libs/common/beets/util/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,28 +14,28 @@ """Miscellaneous utility functions.""" -from __future__ import division, absolute_import, print_function import os import sys import errno import locale import re +import tempfile import shutil import fnmatch -from collections import Counter +import functools +from collections import Counter, namedtuple +from multiprocessing.pool import ThreadPool import traceback import subprocess import platform import shlex from beets.util import hidden -import six from unidecode import unidecode from enum import Enum MAX_FILENAME_LENGTH = 200 -WINDOWS_MAGIC_PREFIX = u'\\\\?\\' -SNI_SUPPORTED = sys.version_info >= (2, 7, 9) +WINDOWS_MAGIC_PREFIX = '\\\\?\\' class HumanReadableException(Exception): @@ -58,27 +57,27 @@ class HumanReadableException(Exception): self.reason = reason self.verb = verb self.tb = tb - super(HumanReadableException, self).__init__(self.get_message()) + super().__init__(self.get_message()) def _gerund(self): """Generate a (likely) gerund form of the English verb. """ - if u' ' in self.verb: + if ' ' in self.verb: return self.verb - gerund = self.verb[:-1] if self.verb.endswith(u'e') else self.verb - gerund += u'ing' + gerund = self.verb[:-1] if self.verb.endswith('e') else self.verb + gerund += 'ing' return gerund def _reasonstr(self): """Get the reason as a string.""" - if isinstance(self.reason, six.text_type): + if isinstance(self.reason, str): return self.reason elif isinstance(self.reason, bytes): return self.reason.decode('utf-8', 'ignore') elif hasattr(self.reason, 'strerror'): # i.e., EnvironmentError return self.reason.strerror else: - return u'"{0}"'.format(six.text_type(self.reason)) + return '"{}"'.format(str(self.reason)) def get_message(self): """Create the human-readable description of the error, sans @@ -92,7 +91,7 @@ class HumanReadableException(Exception): """ if self.tb: logger.debug(self.tb) - logger.error(u'{0}: {1}', self.error_kind, self.args[0]) + logger.error('{0}: {1}', self.error_kind, self.args[0]) class FilesystemError(HumanReadableException): @@ -100,29 +99,30 @@ class FilesystemError(HumanReadableException): via a function in this module. The `paths` field is a sequence of pathnames involved in the operation. """ + def __init__(self, reason, verb, paths, tb=None): self.paths = paths - super(FilesystemError, self).__init__(reason, verb, tb) + super().__init__(reason, verb, tb) def get_message(self): # Use a nicer English phrasing for some specific verbs. if self.verb in ('move', 'copy', 'rename'): - clause = u'while {0} {1} to {2}'.format( + clause = 'while {} {} to {}'.format( self._gerund(), displayable_path(self.paths[0]), displayable_path(self.paths[1]) ) elif self.verb in ('delete', 'write', 'create', 'read'): - clause = u'while {0} {1}'.format( + clause = 'while {} {}'.format( self._gerund(), displayable_path(self.paths[0]) ) else: - clause = u'during {0} of paths {1}'.format( - self.verb, u', '.join(displayable_path(p) for p in self.paths) + clause = 'during {} of paths {}'.format( + self.verb, ', '.join(displayable_path(p) for p in self.paths) ) - return u'{0} {1}'.format(self._reasonstr(), clause) + return f'{self._reasonstr()} {clause}' class MoveOperation(Enum): @@ -132,6 +132,8 @@ class MoveOperation(Enum): COPY = 1 LINK = 2 HARDLINK = 3 + REFLINK = 4 + REFLINK_AUTO = 5 def normpath(path): @@ -182,7 +184,7 @@ def sorted_walk(path, ignore=(), ignore_hidden=False, logger=None): contents = os.listdir(syspath(path)) except OSError as exc: if logger: - logger.warning(u'could not list directory {0}: {1}'.format( + logger.warning('could not list directory {}: {}'.format( displayable_path(path), exc.strerror )) return @@ -195,6 +197,10 @@ def sorted_walk(path, ignore=(), ignore_hidden=False, logger=None): skip = False for pat in ignore: if fnmatch.fnmatch(base, pat): + if logger: + logger.debug('ignoring {} due to ignore rule {}'.format( + base, pat + )) skip = True break if skip: @@ -217,8 +223,14 @@ def sorted_walk(path, ignore=(), ignore_hidden=False, logger=None): for base in dirs: cur = os.path.join(path, base) # yield from sorted_walk(...) - for res in sorted_walk(cur, ignore, ignore_hidden, logger): - yield res + yield from sorted_walk(cur, ignore, ignore_hidden, logger) + + +def path_as_posix(path): + """Return the string representation of the path with forward (/) + slashes. + """ + return path.replace(b'\\', b'/') def mkdirall(path): @@ -229,7 +241,7 @@ def mkdirall(path): if not os.path.isdir(syspath(ancestor)): try: os.mkdir(syspath(ancestor)) - except (OSError, IOError) as exc: + except OSError as exc: raise FilesystemError(exc, 'create', (ancestor,), traceback.format_exc()) @@ -282,13 +294,13 @@ def prune_dirs(path, root=None, clutter=('.DS_Store', 'Thumbs.db')): continue clutter = [bytestring_path(c) for c in clutter] match_paths = [bytestring_path(d) for d in os.listdir(directory)] - if fnmatch_all(match_paths, clutter): - # Directory contains only clutter (or nothing). - try: + try: + if fnmatch_all(match_paths, clutter): + # Directory contains only clutter (or nothing). shutil.rmtree(directory) - except OSError: + else: break - else: + except OSError: break @@ -367,18 +379,18 @@ def bytestring_path(path): PATH_SEP = bytestring_path(os.sep) -def displayable_path(path, separator=u'; '): +def displayable_path(path, separator='; '): """Attempts to decode a bytestring path to a unicode object for the purpose of displaying it to the user. If the `path` argument is a list or a tuple, the elements are joined with `separator`. """ if isinstance(path, (list, tuple)): return separator.join(displayable_path(p) for p in path) - elif isinstance(path, six.text_type): + elif isinstance(path, str): return path elif not isinstance(path, bytes): # A non-string object: just get its unicode representation. - return six.text_type(path) + return str(path) try: return path.decode(_fsencoding(), 'ignore') @@ -397,7 +409,7 @@ def syspath(path, prefix=True): if os.path.__name__ != 'ntpath': return path - if not isinstance(path, six.text_type): + if not isinstance(path, str): # Beets currently represents Windows paths internally with UTF-8 # arbitrarily. But earlier versions used MBCS because it is # reported as the FS encoding by Windows. Try both. @@ -410,11 +422,11 @@ def syspath(path, prefix=True): path = path.decode(encoding, 'replace') # Add the magic prefix if it isn't already there. - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx if prefix and not path.startswith(WINDOWS_MAGIC_PREFIX): - if path.startswith(u'\\\\'): + if path.startswith('\\\\'): # UNC path. Final path should look like \\?\UNC\... - path = u'UNC' + path[1:] + path = 'UNC' + path[1:] path = WINDOWS_MAGIC_PREFIX + path return path @@ -436,7 +448,7 @@ def remove(path, soft=True): return try: os.remove(path) - except (OSError, IOError) as exc: + except OSError as exc: raise FilesystemError(exc, 'delete', (path,), traceback.format_exc()) @@ -451,10 +463,10 @@ def copy(path, dest, replace=False): path = syspath(path) dest = syspath(dest) if not replace and os.path.exists(dest): - raise FilesystemError(u'file exists', 'copy', (path, dest)) + raise FilesystemError('file exists', 'copy', (path, dest)) try: shutil.copyfile(path, dest) - except (OSError, IOError) as exc: + except OSError as exc: raise FilesystemError(exc, 'copy', (path, dest), traceback.format_exc()) @@ -467,24 +479,37 @@ def move(path, dest, replace=False): instead, in which case metadata will *not* be preserved. Paths are translated to system paths. """ + if os.path.isdir(path): + raise FilesystemError(u'source is directory', 'move', (path, dest)) + if os.path.isdir(dest): + raise FilesystemError(u'destination is directory', 'move', + (path, dest)) if samefile(path, dest): return path = syspath(path) dest = syspath(dest) if os.path.exists(dest) and not replace: - raise FilesystemError(u'file exists', 'rename', (path, dest)) + raise FilesystemError('file exists', 'rename', (path, dest)) # First, try renaming the file. try: - os.rename(path, dest) + os.replace(path, dest) except OSError: - # Otherwise, copy and delete the original. + tmp = tempfile.mktemp(suffix='.beets', + prefix=py3_path(b'.' + os.path.basename(dest)), + dir=py3_path(os.path.dirname(dest))) + tmp = syspath(tmp) try: - shutil.copyfile(path, dest) + shutil.copyfile(path, tmp) + os.replace(tmp, dest) + tmp = None os.remove(path) - except (OSError, IOError) as exc: + except OSError as exc: raise FilesystemError(exc, 'move', (path, dest), traceback.format_exc()) + finally: + if tmp is not None: + os.remove(tmp) def link(path, dest, replace=False): @@ -496,18 +521,18 @@ def link(path, dest, replace=False): return if os.path.exists(syspath(dest)) and not replace: - raise FilesystemError(u'file exists', 'rename', (path, dest)) + raise FilesystemError('file exists', 'rename', (path, dest)) try: os.symlink(syspath(path), syspath(dest)) except NotImplementedError: # raised on python >= 3.2 and Windows versions before Vista - raise FilesystemError(u'OS does not support symbolic links.' + raise FilesystemError('OS does not support symbolic links.' 'link', (path, dest), traceback.format_exc()) except OSError as exc: # TODO: Windows version checks can be removed for python 3 if hasattr('sys', 'getwindowsversion'): if sys.getwindowsversion()[0] < 6: # is before Vista - exc = u'OS does not support symbolic links.' + exc = 'OS does not support symbolic links.' raise FilesystemError(exc, 'link', (path, dest), traceback.format_exc()) @@ -521,21 +546,50 @@ def hardlink(path, dest, replace=False): return if os.path.exists(syspath(dest)) and not replace: - raise FilesystemError(u'file exists', 'rename', (path, dest)) + raise FilesystemError('file exists', 'rename', (path, dest)) try: os.link(syspath(path), syspath(dest)) except NotImplementedError: - raise FilesystemError(u'OS does not support hard links.' + raise FilesystemError('OS does not support hard links.' 'link', (path, dest), traceback.format_exc()) except OSError as exc: if exc.errno == errno.EXDEV: - raise FilesystemError(u'Cannot hard link across devices.' + raise FilesystemError('Cannot hard link across devices.' 'link', (path, dest), traceback.format_exc()) else: raise FilesystemError(exc, 'link', (path, dest), traceback.format_exc()) +def reflink(path, dest, replace=False, fallback=False): + """Create a reflink from `dest` to `path`. + + Raise an `OSError` if `dest` already exists, unless `replace` is + True. If `path` == `dest`, then do nothing. + + If reflinking fails and `fallback` is enabled, try copying the file + instead. Otherwise, raise an error without trying a plain copy. + + May raise an `ImportError` if the `reflink` module is not available. + """ + import reflink as pyreflink + + if samefile(path, dest): + return + + if os.path.exists(syspath(dest)) and not replace: + raise FilesystemError('file exists', 'rename', (path, dest)) + + try: + pyreflink.reflink(path, dest) + except (NotImplementedError, pyreflink.ReflinkImpossibleError): + if fallback: + copy(path, dest, replace) + else: + raise FilesystemError('OS/filesystem does not support reflinks.', + 'link', (path, dest), traceback.format_exc()) + + def unique_path(path): """Returns a version of ``path`` that does not exist on the filesystem. Specifically, if ``path` itself already exists, then @@ -553,22 +607,23 @@ def unique_path(path): num = 0 while True: num += 1 - suffix = u'.{}'.format(num).encode() + ext + suffix = f'.{num}'.encode() + ext new_path = base + suffix if not os.path.exists(new_path): return new_path + # Note: The Windows "reserved characters" are, of course, allowed on # Unix. They are forbidden here because they cause problems on Samba # shares, which are sufficiently common as to cause frequent problems. -# http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx +# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx CHAR_REPLACE = [ - (re.compile(r'[\\/]'), u'_'), # / and \ -- forbidden everywhere. - (re.compile(r'^\.'), u'_'), # Leading dot (hidden files on Unix). - (re.compile(r'[\x00-\x1f]'), u''), # Control characters. - (re.compile(r'[<>:"\?\*\|]'), u'_'), # Windows "reserved characters". - (re.compile(r'\.$'), u'_'), # Trailing dots. - (re.compile(r'\s+$'), u''), # Trailing whitespace. + (re.compile(r'[\\/]'), '_'), # / and \ -- forbidden everywhere. + (re.compile(r'^\.'), '_'), # Leading dot (hidden files on Unix). + (re.compile(r'[\x00-\x1f]'), ''), # Control characters. + (re.compile(r'[<>:"\?\*\|]'), '_'), # Windows "reserved characters". + (re.compile(r'\.$'), '_'), # Trailing dots. + (re.compile(r'\s+$'), ''), # Trailing whitespace. ] @@ -692,36 +747,29 @@ def py3_path(path): it is. So this function helps us "smuggle" the true bytes data through APIs that took Python 3's Unicode mandate too seriously. """ - if isinstance(path, six.text_type): + if isinstance(path, str): return path assert isinstance(path, bytes) - if six.PY2: - return path return os.fsdecode(path) def str2bool(value): """Returns a boolean reflecting a human-entered string.""" - return value.lower() in (u'yes', u'1', u'true', u't', u'y') + return value.lower() in ('yes', '1', 'true', 't', 'y') def as_string(value): """Convert a value to a Unicode object for matching with a query. None becomes the empty string. Bytestrings are silently decoded. """ - if six.PY2: - buffer_types = buffer, memoryview # noqa: F821 - else: - buffer_types = memoryview - if value is None: - return u'' - elif isinstance(value, buffer_types): + return '' + elif isinstance(value, memoryview): return bytes(value).decode('utf-8', 'ignore') elif isinstance(value, bytes): return value.decode('utf-8', 'ignore') else: - return six.text_type(value) + return str(value) def text_string(value, encoding='utf-8'): @@ -744,7 +792,7 @@ def plurality(objs): """ c = Counter(objs) if not c: - raise ValueError(u'sequence must be non-empty') + raise ValueError('sequence must be non-empty') return c.most_common(1)[0] @@ -761,7 +809,11 @@ def cpu_count(): num = 0 elif sys.platform == 'darwin': try: - num = int(command_output(['/usr/sbin/sysctl', '-n', 'hw.ncpu'])) + num = int(command_output([ + '/usr/sbin/sysctl', + '-n', + 'hw.ncpu', + ]).stdout) except (ValueError, OSError, subprocess.CalledProcessError): num = 0 else: @@ -781,20 +833,23 @@ def convert_command_args(args): assert isinstance(args, list) def convert(arg): - if six.PY2: - if isinstance(arg, six.text_type): - arg = arg.encode(arg_encoding()) - else: - if isinstance(arg, bytes): - arg = arg.decode(arg_encoding(), 'surrogateescape') + if isinstance(arg, bytes): + arg = arg.decode(arg_encoding(), 'surrogateescape') return arg return [convert(a) for a in args] +# stdout and stderr as bytes +CommandOutput = namedtuple("CommandOutput", ("stdout", "stderr")) + + def command_output(cmd, shell=False): """Runs the command and returns its output after it has exited. + Returns a CommandOutput. The attributes ``stdout`` and ``stderr`` contain + byte strings of the respective output streams. + ``cmd`` is a list of arguments starting with the command names. The arguments are bytes on Unix and strings on Windows. If ``shell`` is true, ``cmd`` is assumed to be a string and passed to a @@ -829,7 +884,7 @@ def command_output(cmd, shell=False): cmd=' '.join(cmd), output=stdout + stderr, ) - return stdout + return CommandOutput(stdout, stderr) def max_filename_length(path, limit=MAX_FILENAME_LENGTH): @@ -876,25 +931,6 @@ def editor_command(): return open_anything() -def shlex_split(s): - """Split a Unicode or bytes string according to shell lexing rules. - - Raise `ValueError` if the string is not a well-formed shell string. - This is a workaround for a bug in some versions of Python. - """ - if not six.PY2 or isinstance(s, bytes): # Shlex works fine. - return shlex.split(s) - - elif isinstance(s, six.text_type): - # Work around a Python bug. - # http://bugs.python.org/issue6988 - bs = s.encode('utf-8') - return [c.decode('utf-8') for c in shlex.split(bs)] - - else: - raise TypeError(u'shlex_split called with non-string') - - def interactive_open(targets, command): """Open the files in `targets` by `exec`ing a new `command`, given as a Unicode string. (The new program takes over, and Python @@ -906,7 +942,7 @@ def interactive_open(targets, command): # Split the command string into its arguments. try: - args = shlex_split(command) + args = shlex.split(command) except ValueError: # Malformed shell tokens. args = [command] @@ -921,7 +957,7 @@ def _windows_long_path_name(short_path): """Use Windows' `GetLongPathNameW` via ctypes to get the canonical, long path given a short filename. """ - if not isinstance(short_path, six.text_type): + if not isinstance(short_path, str): short_path = short_path.decode(_fsencoding()) import ctypes @@ -982,7 +1018,7 @@ def raw_seconds_short(string): """ match = re.match(r'^(\d+):([0-5]\d)$', string) if not match: - raise ValueError(u'String not in M:SS format') + raise ValueError('String not in M:SS format') minutes, seconds = map(int, match.groups()) return float(minutes * 60 + seconds) @@ -1009,3 +1045,59 @@ def asciify_path(path, sep_replace): sep_replace ) return os.sep.join(path_components) + + +def par_map(transform, items): + """Apply the function `transform` to all the elements in the + iterable `items`, like `map(transform, items)` but with no return + value. The map *might* happen in parallel: it's parallel on Python 3 + and sequential on Python 2. + + The parallelism uses threads (not processes), so this is only useful + for IO-bound `transform`s. + """ + pool = ThreadPool() + pool.map(transform, items) + pool.close() + pool.join() + + +def lazy_property(func): + """A decorator that creates a lazily evaluated property. On first access, + the property is assigned the return value of `func`. This first value is + stored, so that future accesses do not have to evaluate `func` again. + + This behaviour is useful when `func` is expensive to evaluate, and it is + not certain that the result will be needed. + """ + field_name = '_' + func.__name__ + + @property + @functools.wraps(func) + def wrapper(self): + if hasattr(self, field_name): + return getattr(self, field_name) + + value = func(self) + setattr(self, field_name, value) + return value + + return wrapper + + +def decode_commandline_path(path): + """Prepare a path for substitution into commandline template. + + On Python 3, we need to construct the subprocess commands to invoke as a + Unicode string. On Unix, this is a little unfortunate---the OS is + expecting bytes---so we use surrogate escaping and decode with the + argument encoding, which is the same encoding that will then be + *reversed* to recover the same bytes before invoking the OS. On + Windows, we want to preserve the Unicode filename "as is." + """ + # On Python 3, the template is a Unicode string, which only supports + # substitution of Unicode variables. + if platform.system() == 'Windows': + return path.decode(_fsencoding()) + else: + return path.decode(arg_encoding(), 'surrogateescape') diff --git a/libs/common/beets/util/artresizer.py b/libs/common/beets/util/artresizer.py index e5117a6a..8683e228 100644 --- a/libs/common/beets/util/artresizer.py +++ b/libs/common/beets/util/artresizer.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Fabrice Laporte # @@ -16,38 +15,39 @@ """Abstraction layer to resize images using PIL, ImageMagick, or a public resizing proxy if neither is available. """ -from __future__ import division, absolute_import, print_function import subprocess import os +import os.path import re from tempfile import NamedTemporaryFile -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from beets import logging from beets import util -import six # Resizing methods PIL = 1 IMAGEMAGICK = 2 WEBPROXY = 3 -if util.SNI_SUPPORTED: - PROXY_URL = 'https://images.weserv.nl/' -else: - PROXY_URL = 'http://images.weserv.nl/' +PROXY_URL = 'https://images.weserv.nl/' log = logging.getLogger('beets') -def resize_url(url, maxwidth): +def resize_url(url, maxwidth, quality=0): """Return a proxied image URL that resizes the original image to maxwidth (preserving aspect ratio). """ - return '{0}?{1}'.format(PROXY_URL, urlencode({ + params = { 'url': url.replace('http://', ''), 'w': maxwidth, - })) + } + + if quality > 0: + params['q'] = quality + + return '{}?{}'.format(PROXY_URL, urlencode(params)) def temp_file_for(path): @@ -59,48 +59,102 @@ def temp_file_for(path): return util.bytestring_path(f.name) -def pil_resize(maxwidth, path_in, path_out=None): +def pil_resize(maxwidth, path_in, path_out=None, quality=0, max_filesize=0): """Resize using Python Imaging Library (PIL). Return the output path of resized image. """ path_out = path_out or temp_file_for(path_in) from PIL import Image - log.debug(u'artresizer: PIL resizing {0} to {1}', + + log.debug('artresizer: PIL resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) try: im = Image.open(util.syspath(path_in)) size = maxwidth, maxwidth im.thumbnail(size, Image.ANTIALIAS) - im.save(path_out) - return path_out - except IOError: - log.error(u"PIL cannot create thumbnail for '{0}'", + + if quality == 0: + # Use PIL's default quality. + quality = -1 + + # progressive=False only affects JPEGs and is the default, + # but we include it here for explicitness. + im.save(util.py3_path(path_out), quality=quality, progressive=False) + + if max_filesize > 0: + # If maximum filesize is set, we attempt to lower the quality of + # jpeg conversion by a proportional amount, up to 3 attempts + # First, set the maximum quality to either provided, or 95 + if quality > 0: + lower_qual = quality + else: + lower_qual = 95 + for i in range(5): + # 5 attempts is an abitrary choice + filesize = os.stat(util.syspath(path_out)).st_size + log.debug("PIL Pass {0} : Output size: {1}B", i, filesize) + if filesize <= max_filesize: + return path_out + # The relationship between filesize & quality will be + # image dependent. + lower_qual -= 10 + # Restrict quality dropping below 10 + if lower_qual < 10: + lower_qual = 10 + # Use optimize flag to improve filesize decrease + im.save(util.py3_path(path_out), quality=lower_qual, + optimize=True, progressive=False) + log.warning("PIL Failed to resize file to below {0}B", + max_filesize) + return path_out + + else: + return path_out + except OSError: + log.error("PIL cannot create thumbnail for '{0}'", util.displayable_path(path_in)) return path_in -def im_resize(maxwidth, path_in, path_out=None): - """Resize using ImageMagick's ``convert`` tool. - Return the output path of resized image. +def im_resize(maxwidth, path_in, path_out=None, quality=0, max_filesize=0): + """Resize using ImageMagick. + + Use the ``magick`` program or ``convert`` on older versions. Return + the output path of resized image. """ path_out = path_out or temp_file_for(path_in) - log.debug(u'artresizer: ImageMagick resizing {0} to {1}', + log.debug('artresizer: ImageMagick resizing {0} to {1}', util.displayable_path(path_in), util.displayable_path(path_out)) # "-resize WIDTHx>" shrinks images with the width larger # than the given width while maintaining the aspect ratio # with regards to the height. + # ImageMagick already seems to default to no interlace, but we include it + # here for the sake of explicitness. + cmd = ArtResizer.shared.im_convert_cmd + [ + util.syspath(path_in, prefix=False), + '-resize', f'{maxwidth}x>', + '-interlace', 'none', + ] + + if quality > 0: + cmd += ['-quality', f'{quality}'] + + # "-define jpeg:extent=SIZEb" sets the target filesize for imagemagick to + # SIZE in bytes. + if max_filesize > 0: + cmd += ['-define', f'jpeg:extent={max_filesize}b'] + + cmd.append(util.syspath(path_out, prefix=False)) + try: - util.command_output([ - 'convert', util.syspath(path_in, prefix=False), - '-resize', '{0}x>'.format(maxwidth), - util.syspath(path_out, prefix=False), - ]) + util.command_output(cmd) except subprocess.CalledProcessError: - log.warning(u'artresizer: IM convert failed for {0}', + log.warning('artresizer: IM convert failed for {0}', util.displayable_path(path_in)) return path_in + return path_out @@ -112,31 +166,33 @@ BACKEND_FUNCS = { def pil_getsize(path_in): from PIL import Image + try: im = Image.open(util.syspath(path_in)) return im.size - except IOError as exc: - log.error(u"PIL could not read file {}: {}", + except OSError as exc: + log.error("PIL could not read file {}: {}", util.displayable_path(path_in), exc) def im_getsize(path_in): - cmd = ['identify', '-format', '%w %h', - util.syspath(path_in, prefix=False)] + cmd = ArtResizer.shared.im_identify_cmd + \ + ['-format', '%w %h', util.syspath(path_in, prefix=False)] + try: - out = util.command_output(cmd) + out = util.command_output(cmd).stdout except subprocess.CalledProcessError as exc: - log.warning(u'ImageMagick size query failed') + log.warning('ImageMagick size query failed') log.debug( - u'`convert` exited with (status {}) when ' - u'getting size with command {}:\n{}', + '`convert` exited with (status {}) when ' + 'getting size with command {}:\n{}', exc.returncode, cmd, exc.output.strip() ) return try: return tuple(map(int, out.split(b' '))) except IndexError: - log.warning(u'Could not understand IM output: {0!r}', out) + log.warning('Could not understand IM output: {0!r}', out) BACKEND_GET_SIZE = { @@ -145,14 +201,115 @@ BACKEND_GET_SIZE = { } +def pil_deinterlace(path_in, path_out=None): + path_out = path_out or temp_file_for(path_in) + from PIL import Image + + try: + im = Image.open(util.syspath(path_in)) + im.save(util.py3_path(path_out), progressive=False) + return path_out + except IOError: + return path_in + + +def im_deinterlace(path_in, path_out=None): + path_out = path_out or temp_file_for(path_in) + + cmd = ArtResizer.shared.im_convert_cmd + [ + util.syspath(path_in, prefix=False), + '-interlace', 'none', + util.syspath(path_out, prefix=False), + ] + + try: + util.command_output(cmd) + return path_out + except subprocess.CalledProcessError: + return path_in + + +DEINTERLACE_FUNCS = { + PIL: pil_deinterlace, + IMAGEMAGICK: im_deinterlace, +} + + +def im_get_format(filepath): + cmd = ArtResizer.shared.im_identify_cmd + [ + '-format', '%[magick]', + util.syspath(filepath) + ] + + try: + return util.command_output(cmd).stdout + except subprocess.CalledProcessError: + return None + + +def pil_get_format(filepath): + from PIL import Image, UnidentifiedImageError + + try: + with Image.open(util.syspath(filepath)) as im: + return im.format + except (ValueError, TypeError, UnidentifiedImageError, FileNotFoundError): + log.exception("failed to detect image format for {}", filepath) + return None + + +BACKEND_GET_FORMAT = { + PIL: pil_get_format, + IMAGEMAGICK: im_get_format, +} + + +def im_convert_format(source, target, deinterlaced): + cmd = ArtResizer.shared.im_convert_cmd + [ + util.syspath(source), + *(["-interlace", "none"] if deinterlaced else []), + util.syspath(target), + ] + + try: + subprocess.check_call( + cmd, + stderr=subprocess.DEVNULL, + stdout=subprocess.DEVNULL + ) + return target + except subprocess.CalledProcessError: + return source + + +def pil_convert_format(source, target, deinterlaced): + from PIL import Image, UnidentifiedImageError + + try: + with Image.open(util.syspath(source)) as im: + im.save(util.py3_path(target), progressive=not deinterlaced) + return target + except (ValueError, TypeError, UnidentifiedImageError, FileNotFoundError, + OSError): + log.exception("failed to convert image {} -> {}", source, target) + return source + + +BACKEND_CONVERT_IMAGE_FORMAT = { + PIL: pil_convert_format, + IMAGEMAGICK: im_convert_format, +} + + class Shareable(type): """A pseudo-singleton metaclass that allows both shared and non-shared instances. The ``MyClass.shared`` property holds a lazily-created shared instance of ``MyClass`` while calling ``MyClass()`` to construct a new object works as usual. """ + def __init__(cls, name, bases, dict): - super(Shareable, cls).__init__(name, bases, dict) + super().__init__(name, bases, dict) cls._instance = None @property @@ -162,7 +319,7 @@ class Shareable(type): return cls._instance -class ArtResizer(six.with_metaclass(Shareable, object)): +class ArtResizer(metaclass=Shareable): """A singleton class that performs image resizes. """ @@ -170,21 +327,44 @@ class ArtResizer(six.with_metaclass(Shareable, object)): """Create a resizer object with an inferred method. """ self.method = self._check_method() - log.debug(u"artresizer: method is {0}", self.method) + log.debug("artresizer: method is {0}", self.method) self.can_compare = self._can_compare() - def resize(self, maxwidth, path_in, path_out=None): + # Use ImageMagick's magick binary when it's available. If it's + # not, fall back to the older, separate convert and identify + # commands. + if self.method[0] == IMAGEMAGICK: + self.im_legacy = self.method[2] + if self.im_legacy: + self.im_convert_cmd = ['convert'] + self.im_identify_cmd = ['identify'] + else: + self.im_convert_cmd = ['magick'] + self.im_identify_cmd = ['magick', 'identify'] + + def resize( + self, maxwidth, path_in, path_out=None, quality=0, max_filesize=0 + ): """Manipulate an image file according to the method, returning a new path. For PIL or IMAGEMAGIC methods, resizes the image to a - temporary file. For WEBPROXY, returns `path_in` unmodified. + temporary file and encodes with the specified quality level. + For WEBPROXY, returns `path_in` unmodified. """ if self.local: func = BACKEND_FUNCS[self.method[0]] - return func(maxwidth, path_in, path_out) + return func(maxwidth, path_in, path_out, + quality=quality, max_filesize=max_filesize) else: return path_in - def proxy_url(self, maxwidth, url): + def deinterlace(self, path_in, path_out=None): + if self.local: + func = DEINTERLACE_FUNCS[self.method[0]] + return func(path_in, path_out) + else: + return path_in + + def proxy_url(self, maxwidth, url, quality=0): """Modifies an image URL according the method, returning a new URL. For WEBPROXY, a URL on the proxy server is returned. Otherwise, the URL is returned unmodified. @@ -192,7 +372,7 @@ class ArtResizer(six.with_metaclass(Shareable, object)): if self.local: return url else: - return resize_url(url, maxwidth) + return resize_url(url, maxwidth, quality) @property def local(self): @@ -205,12 +385,50 @@ class ArtResizer(six.with_metaclass(Shareable, object)): """Return the size of an image file as an int couple (width, height) in pixels. - Only available locally + Only available locally. """ if self.local: func = BACKEND_GET_SIZE[self.method[0]] return func(path_in) + def get_format(self, path_in): + """Returns the format of the image as a string. + + Only available locally. + """ + if self.local: + func = BACKEND_GET_FORMAT[self.method[0]] + return func(path_in) + + def reformat(self, path_in, new_format, deinterlaced=True): + """Converts image to desired format, updating its extension, but + keeping the same filename. + + Only available locally. + """ + if not self.local: + return path_in + + new_format = new_format.lower() + # A nonexhaustive map of image "types" to extensions overrides + new_format = { + 'jpeg': 'jpg', + }.get(new_format, new_format) + + fname, ext = os.path.splitext(path_in) + path_new = fname + b'.' + new_format.encode('utf8') + func = BACKEND_CONVERT_IMAGE_FORMAT[self.method[0]] + + # allows the exception to propagate, while still making sure a changed + # file path was removed + result_path = path_in + try: + result_path = func(path_in, path_new, deinterlaced) + finally: + if result_path != path_in: + os.unlink(path_in) + return result_path + def _can_compare(self): """A boolean indicating whether image comparison is available""" @@ -218,10 +436,20 @@ class ArtResizer(six.with_metaclass(Shareable, object)): @staticmethod def _check_method(): - """Return a tuple indicating an available method and its version.""" + """Return a tuple indicating an available method and its version. + + The result has at least two elements: + - The method, eitehr WEBPROXY, PIL, or IMAGEMAGICK. + - The version. + + If the method is IMAGEMAGICK, there is also a third element: a + bool flag indicating whether to use the `magick` binary or + legacy single-purpose executables (`convert`, `identify`, etc.) + """ version = get_im_version() if version: - return IMAGEMAGICK, version + version, legacy = version + return IMAGEMAGICK, version, legacy version = get_pil_version() if version: @@ -231,31 +459,34 @@ class ArtResizer(six.with_metaclass(Shareable, object)): def get_im_version(): - """Return Image Magick version or None if it is unavailable - Try invoking ImageMagick's "convert". + """Get the ImageMagick version and legacy flag as a pair. Or return + None if ImageMagick is not available. """ - try: - out = util.command_output(['convert', '--version']) + for cmd_name, legacy in ((['magick'], False), (['convert'], True)): + cmd = cmd_name + ['--version'] - if b'imagemagick' in out.lower(): - pattern = br".+ (\d+)\.(\d+)\.(\d+).*" - match = re.search(pattern, out) - if match: - return (int(match.group(1)), - int(match.group(2)), - int(match.group(3))) - return (0,) + try: + out = util.command_output(cmd).stdout + except (subprocess.CalledProcessError, OSError) as exc: + log.debug('ImageMagick version check failed: {}', exc) + else: + if b'imagemagick' in out.lower(): + pattern = br".+ (\d+)\.(\d+)\.(\d+).*" + match = re.search(pattern, out) + if match: + version = (int(match.group(1)), + int(match.group(2)), + int(match.group(3))) + return version, legacy - except (subprocess.CalledProcessError, OSError) as exc: - log.debug(u'ImageMagick check `convert --version` failed: {}', exc) - return None + return None def get_pil_version(): - """Return Image Magick version or None if it is unavailable - Try importing PIL.""" + """Get the PIL/Pillow version, or None if it is unavailable. + """ try: - __import__('PIL', fromlist=[str('Image')]) + __import__('PIL', fromlist=['Image']) return (0,) except ImportError: return None diff --git a/libs/common/beets/util/bluelet.py b/libs/common/beets/util/bluelet.py index 0da17559..a40f3b2f 100644 --- a/libs/common/beets/util/bluelet.py +++ b/libs/common/beets/util/bluelet.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Extremely simple pure-Python implementation of coroutine-style asynchronous socket I/O. Inspired by, but inferior to, Eventlet. Bluelet can also be thought of as a less-terrible replacement for @@ -7,9 +5,7 @@ asyncore. Bluelet: easy concurrency without all the messy parallelism. """ -from __future__ import division, absolute_import, print_function -import six import socket import select import sys @@ -22,7 +18,7 @@ import collections # Basic events used for thread scheduling. -class Event(object): +class Event: """Just a base class identifying Bluelet events. An event is an object yielded from a Bluelet thread coroutine to suspend operation and communicate with the scheduler. @@ -201,7 +197,7 @@ class ThreadException(Exception): self.exc_info = exc_info def reraise(self): - six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2]) + raise self.exc_info[1].with_traceback(self.exc_info[2]) SUSPENDED = Event() # Special sentinel placeholder for suspended threads. @@ -336,16 +332,20 @@ def run(root_coro): break # Wait and fire. - event2coro = dict((v, k) for k, v in threads.items()) + event2coro = {v: k for k, v in threads.items()} for event in _event_select(threads.values()): # Run the IO operation, but catch socket errors. try: value = event.fire() - except socket.error as exc: + except OSError as exc: if isinstance(exc.args, tuple) and \ exc.args[0] == errno.EPIPE: # Broken pipe. Remote host disconnected. pass + elif isinstance(exc.args, tuple) and \ + exc.args[0] == errno.ECONNRESET: + # Connection was reset by peer. + pass else: traceback.print_exc() # Abort the coroutine. @@ -386,7 +386,7 @@ class SocketClosedError(Exception): pass -class Listener(object): +class Listener: """A socket wrapper object for listening sockets. """ def __init__(self, host, port): @@ -416,7 +416,7 @@ class Listener(object): self.sock.close() -class Connection(object): +class Connection: """A socket wrapper object for connected sockets. """ def __init__(self, sock, addr): @@ -541,7 +541,7 @@ def spawn(coro): and child coroutines run concurrently. """ if not isinstance(coro, types.GeneratorType): - raise ValueError(u'%s is not a coroutine' % coro) + raise ValueError('%s is not a coroutine' % coro) return SpawnEvent(coro) @@ -551,7 +551,7 @@ def call(coro): returns a value using end(), then this event returns that value. """ if not isinstance(coro, types.GeneratorType): - raise ValueError(u'%s is not a coroutine' % coro) + raise ValueError('%s is not a coroutine' % coro) return DelegationEvent(coro) diff --git a/libs/common/beets/util/confit.py b/libs/common/beets/util/confit.py index b5513f48..dd912c44 100644 --- a/libs/common/beets/util/confit.py +++ b/libs/common/beets/util/confit.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- -# This file is part of Confuse. -# Copyright 2016, Adrian Sampson. +# This file is part of beets. +# Copyright 2016-2019, Adrian Sampson. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -13,1501 +12,17 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""Worry-free YAML configuration files. -""" -from __future__ import division, absolute_import, print_function -import platform -import os -import pkgutil -import sys -import yaml -import collections -import re -from collections import OrderedDict +import confuse -UNIX_DIR_VAR = 'XDG_CONFIG_HOME' -UNIX_DIR_FALLBACK = '~/.config' -WINDOWS_DIR_VAR = 'APPDATA' -WINDOWS_DIR_FALLBACK = '~\\AppData\\Roaming' -MAC_DIR = '~/Library/Application Support' +import warnings +warnings.warn("beets.util.confit is deprecated; use confuse instead") -CONFIG_FILENAME = 'config.yaml' -DEFAULT_FILENAME = 'config_default.yaml' -ROOT_NAME = 'root' +# Import everything from the confuse module into this module. +for key, value in confuse.__dict__.items(): + if key not in ['__name__']: + globals()[key] = value -YAML_TAB_PROBLEM = "found character '\\t' that cannot start any token" -REDACTED_TOMBSTONE = 'REDACTED' - - -# Utilities. - -PY3 = sys.version_info[0] == 3 -STRING = str if PY3 else unicode # noqa: F821 -BASESTRING = str if PY3 else basestring # noqa: F821 -NUMERIC_TYPES = (int, float) if PY3 else (int, float, long) # noqa: F821 - - -def iter_first(sequence): - """Get the first element from an iterable or raise a ValueError if - the iterator generates no values. - """ - it = iter(sequence) - try: - return next(it) - except StopIteration: - raise ValueError() - - -# Exceptions. - -class ConfigError(Exception): - """Base class for exceptions raised when querying a configuration. - """ - - -class NotFoundError(ConfigError): - """A requested value could not be found in the configuration trees. - """ - - -class ConfigValueError(ConfigError): - """The value in the configuration is illegal.""" - - -class ConfigTypeError(ConfigValueError): - """The value in the configuration did not match the expected type. - """ - - -class ConfigTemplateError(ConfigError): - """Base class for exceptions raised because of an invalid template. - """ - - -class ConfigReadError(ConfigError): - """A configuration file could not be read.""" - def __init__(self, filename, reason=None): - self.filename = filename - self.reason = reason - - message = u'file {0} could not be read'.format(filename) - if isinstance(reason, yaml.scanner.ScannerError) and \ - reason.problem == YAML_TAB_PROBLEM: - # Special-case error message for tab indentation in YAML markup. - message += u': found tab character at line {0}, column {1}'.format( - reason.problem_mark.line + 1, - reason.problem_mark.column + 1, - ) - elif reason: - # Generic error message uses exception's message. - message += u': {0}'.format(reason) - - super(ConfigReadError, self).__init__(message) - - -# Views and sources. - -class ConfigSource(dict): - """A dictionary augmented with metadata about the source of the - configuration. - """ - def __init__(self, value, filename=None, default=False): - super(ConfigSource, self).__init__(value) - if filename is not None and not isinstance(filename, BASESTRING): - raise TypeError(u'filename must be a string or None') - self.filename = filename - self.default = default - - def __repr__(self): - return 'ConfigSource({0!r}, {1!r}, {2!r})'.format( - super(ConfigSource, self), - self.filename, - self.default, - ) - - @classmethod - def of(cls, value): - """Given either a dictionary or a `ConfigSource` object, return - a `ConfigSource` object. This lets a function accept either type - of object as an argument. - """ - if isinstance(value, ConfigSource): - return value - elif isinstance(value, dict): - return ConfigSource(value) - else: - raise TypeError(u'source value must be a dict') - - -class ConfigView(object): - """A configuration "view" is a query into a program's configuration - data. A view represents a hypothetical location in the configuration - tree; to extract the data from the location, a client typically - calls the ``view.get()`` method. The client can access children in - the tree (subviews) by subscripting the parent view (i.e., - ``view[key]``). - """ - - name = None - """The name of the view, depicting the path taken through the - configuration in Python-like syntax (e.g., ``foo['bar'][42]``). - """ - - def resolve(self): - """The core (internal) data retrieval method. Generates (value, - source) pairs for each source that contains a value for this - view. May raise ConfigTypeError if a type error occurs while - traversing a source. - """ - raise NotImplementedError - - def first(self): - """Return a (value, source) pair for the first object found for - this view. This amounts to the first element returned by - `resolve`. If no values are available, a NotFoundError is - raised. - """ - pairs = self.resolve() - try: - return iter_first(pairs) - except ValueError: - raise NotFoundError(u"{0} not found".format(self.name)) - - def exists(self): - """Determine whether the view has a setting in any source. - """ - try: - self.first() - except NotFoundError: - return False - return True - - def add(self, value): - """Set the *default* value for this configuration view. The - specified value is added as the lowest-priority configuration - data source. - """ - raise NotImplementedError - - def set(self, value): - """*Override* the value for this configuration view. The - specified value is added as the highest-priority configuration - data source. - """ - raise NotImplementedError - - def root(self): - """The RootView object from which this view is descended. - """ - raise NotImplementedError - - def __repr__(self): - return '<{}: {}>'.format(self.__class__.__name__, self.name) - - def __iter__(self): - """Iterate over the keys of a dictionary view or the *subviews* - of a list view. - """ - # Try getting the keys, if this is a dictionary view. - try: - keys = self.keys() - for key in keys: - yield key - - except ConfigTypeError: - # Otherwise, try iterating over a list. - collection = self.get() - if not isinstance(collection, (list, tuple)): - raise ConfigTypeError( - u'{0} must be a dictionary or a list, not {1}'.format( - self.name, type(collection).__name__ - ) - ) - - # Yield all the indices in the list. - for index in range(len(collection)): - yield self[index] - - def __getitem__(self, key): - """Get a subview of this view.""" - return Subview(self, key) - - def __setitem__(self, key, value): - """Create an overlay source to assign a given key under this - view. - """ - self.set({key: value}) - - def __contains__(self, key): - return self[key].exists() - - def set_args(self, namespace): - """Overlay parsed command-line arguments, generated by a library - like argparse or optparse, onto this view's value. ``namespace`` - can be a ``dict`` or namespace object. - """ - args = {} - if isinstance(namespace, dict): - items = namespace.items() - else: - items = namespace.__dict__.items() - for key, value in items: - if value is not None: # Avoid unset options. - args[key] = value - self.set(args) - - # Magical conversions. These special methods make it possible to use - # View objects somewhat transparently in certain circumstances. For - # example, rather than using ``view.get(bool)``, it's possible to - # just say ``bool(view)`` or use ``view`` in a conditional. - - def __str__(self): - """Get the value for this view as a bytestring. - """ - if PY3: - return self.__unicode__() - else: - return bytes(self.get()) - - def __unicode__(self): - """Get the value for this view as a Unicode string. - """ - return STRING(self.get()) - - def __nonzero__(self): - """Gets the value for this view as a boolean. (Python 2 only.) - """ - return self.__bool__() - - def __bool__(self): - """Gets the value for this view as a boolean. (Python 3 only.) - """ - return bool(self.get()) - - # Dictionary emulation methods. - - def keys(self): - """Returns a list containing all the keys available as subviews - of the current views. This enumerates all the keys in *all* - dictionaries matching the current view, in contrast to - ``view.get(dict).keys()``, which gets all the keys for the - *first* dict matching the view. If the object for this view in - any source is not a dict, then a ConfigTypeError is raised. The - keys are ordered according to how they appear in each source. - """ - keys = [] - - for dic, _ in self.resolve(): - try: - cur_keys = dic.keys() - except AttributeError: - raise ConfigTypeError( - u'{0} must be a dict, not {1}'.format( - self.name, type(dic).__name__ - ) - ) - - for key in cur_keys: - if key not in keys: - keys.append(key) - - return keys - - def items(self): - """Iterates over (key, subview) pairs contained in dictionaries - from *all* sources at this view. If the object for this view in - any source is not a dict, then a ConfigTypeError is raised. - """ - for key in self.keys(): - yield key, self[key] - - def values(self): - """Iterates over all the subviews contained in dictionaries from - *all* sources at this view. If the object for this view in any - source is not a dict, then a ConfigTypeError is raised. - """ - for key in self.keys(): - yield self[key] - - # List/sequence emulation. - - def all_contents(self): - """Iterates over all subviews from collections at this view from - *all* sources. If the object for this view in any source is not - iterable, then a ConfigTypeError is raised. This method is - intended to be used when the view indicates a list; this method - will concatenate the contents of the list from all sources. - """ - for collection, _ in self.resolve(): - try: - it = iter(collection) - except TypeError: - raise ConfigTypeError( - u'{0} must be an iterable, not {1}'.format( - self.name, type(collection).__name__ - ) - ) - for value in it: - yield value - - # Validation and conversion. - - def flatten(self, redact=False): - """Create a hierarchy of OrderedDicts containing the data from - this view, recursively reifying all views to get their - represented values. - - If `redact` is set, then sensitive values are replaced with - the string "REDACTED". - """ - od = OrderedDict() - for key, view in self.items(): - if redact and view.redact: - od[key] = REDACTED_TOMBSTONE - else: - try: - od[key] = view.flatten(redact=redact) - except ConfigTypeError: - od[key] = view.get() - return od - - def get(self, template=None): - """Retrieve the value for this view according to the template. - - The `template` against which the values are checked can be - anything convertible to a `Template` using `as_template`. This - means you can pass in a default integer or string value, for - example, or a type to just check that something matches the type - you expect. - - May raise a `ConfigValueError` (or its subclass, - `ConfigTypeError`) or a `NotFoundError` when the configuration - doesn't satisfy the template. - """ - return as_template(template).value(self, template) - - # Shortcuts for common templates. - - def as_filename(self): - """Get the value as a path. Equivalent to `get(Filename())`. - """ - return self.get(Filename()) - - def as_choice(self, choices): - """Get the value from a list of choices. Equivalent to - `get(Choice(choices))`. - """ - return self.get(Choice(choices)) - - def as_number(self): - """Get the value as any number type: int or float. Equivalent to - `get(Number())`. - """ - return self.get(Number()) - - def as_str_seq(self, split=True): - """Get the value as a sequence of strings. Equivalent to - `get(StrSeq())`. - """ - return self.get(StrSeq(split=split)) - - def as_pairs(self, default_value=None): - """Get the value as a sequence of pairs of two strings. Equivalent to - `get(Pairs())`. - """ - return self.get(Pairs(default_value=default_value)) - - def as_str(self): - """Get the value as a (Unicode) string. Equivalent to - `get(unicode)` on Python 2 and `get(str)` on Python 3. - """ - return self.get(String()) - - # Redaction. - - @property - def redact(self): - """Whether the view contains sensitive information and should be - redacted from output. - """ - return () in self.get_redactions() - - @redact.setter - def redact(self, flag): - self.set_redaction((), flag) - - def set_redaction(self, path, flag): - """Add or remove a redaction for a key path, which should be an - iterable of keys. - """ - raise NotImplementedError() - - def get_redactions(self): - """Get the set of currently-redacted sub-key-paths at this view. - """ - raise NotImplementedError() - - -class RootView(ConfigView): - """The base of a view hierarchy. This view keeps track of the - sources that may be accessed by subviews. - """ - def __init__(self, sources): - """Create a configuration hierarchy for a list of sources. At - least one source must be provided. The first source in the list - has the highest priority. - """ - self.sources = list(sources) - self.name = ROOT_NAME - self.redactions = set() - - def add(self, obj): - self.sources.append(ConfigSource.of(obj)) - - def set(self, value): - self.sources.insert(0, ConfigSource.of(value)) - - def resolve(self): - return ((dict(s), s) for s in self.sources) - - def clear(self): - """Remove all sources (and redactions) from this - configuration. - """ - del self.sources[:] - self.redactions.clear() - - def root(self): - return self - - def set_redaction(self, path, flag): - if flag: - self.redactions.add(path) - elif path in self.redactions: - self.redactions.remove(path) - - def get_redactions(self): - return self.redactions - - -class Subview(ConfigView): - """A subview accessed via a subscript of a parent view.""" - def __init__(self, parent, key): - """Make a subview of a parent view for a given subscript key. - """ - self.parent = parent - self.key = key - - # Choose a human-readable name for this view. - if isinstance(self.parent, RootView): - self.name = '' - else: - self.name = self.parent.name - if not isinstance(self.key, int): - self.name += '.' - if isinstance(self.key, int): - self.name += u'#{0}'.format(self.key) - elif isinstance(self.key, bytes): - self.name += self.key.decode('utf-8') - elif isinstance(self.key, STRING): - self.name += self.key - else: - self.name += repr(self.key) - - def resolve(self): - for collection, source in self.parent.resolve(): - try: - value = collection[self.key] - except IndexError: - # List index out of bounds. - continue - except KeyError: - # Dict key does not exist. - continue - except TypeError: - # Not subscriptable. - raise ConfigTypeError( - u"{0} must be a collection, not {1}".format( - self.parent.name, type(collection).__name__ - ) - ) - yield value, source - - def set(self, value): - self.parent.set({self.key: value}) - - def add(self, value): - self.parent.add({self.key: value}) - - def root(self): - return self.parent.root() - - def set_redaction(self, path, flag): - self.parent.set_redaction((self.key,) + path, flag) - - def get_redactions(self): - return (kp[1:] for kp in self.parent.get_redactions() - if kp and kp[0] == self.key) - - -# Config file paths, including platform-specific paths and in-package -# defaults. - -# Based on get_root_path from Flask by Armin Ronacher. -def _package_path(name): - """Returns the path to the package containing the named module or - None if the path could not be identified (e.g., if - ``name == "__main__"``). - """ - loader = pkgutil.get_loader(name) - if loader is None or name == '__main__': - return None - - if hasattr(loader, 'get_filename'): - filepath = loader.get_filename(name) - else: - # Fall back to importing the specified module. - __import__(name) - filepath = sys.modules[name].__file__ - - return os.path.dirname(os.path.abspath(filepath)) - - -def config_dirs(): - """Return a platform-specific list of candidates for user - configuration directories on the system. - - The candidates are in order of priority, from highest to lowest. The - last element is the "fallback" location to be used when no - higher-priority config file exists. - """ - paths = [] - - if platform.system() == 'Darwin': - paths.append(MAC_DIR) - paths.append(UNIX_DIR_FALLBACK) - if UNIX_DIR_VAR in os.environ: - paths.append(os.environ[UNIX_DIR_VAR]) - - elif platform.system() == 'Windows': - paths.append(WINDOWS_DIR_FALLBACK) - if WINDOWS_DIR_VAR in os.environ: - paths.append(os.environ[WINDOWS_DIR_VAR]) - - else: - # Assume Unix. - paths.append(UNIX_DIR_FALLBACK) - if UNIX_DIR_VAR in os.environ: - paths.append(os.environ[UNIX_DIR_VAR]) - - # Expand and deduplicate paths. - out = [] - for path in paths: - path = os.path.abspath(os.path.expanduser(path)) - if path not in out: - out.append(path) - return out - - -# YAML loading. - -class Loader(yaml.SafeLoader): - """A customized YAML loader. This loader deviates from the official - YAML spec in a few convenient ways: - - - All strings as are Unicode objects. - - All maps are OrderedDicts. - - Strings can begin with % without quotation. - """ - # All strings should be Unicode objects, regardless of contents. - def _construct_unicode(self, node): - return self.construct_scalar(node) - - # Use ordered dictionaries for every YAML map. - # From https://gist.github.com/844388 - def construct_yaml_map(self, node): - data = OrderedDict() - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_mapping(self, node, deep=False): - if isinstance(node, yaml.MappingNode): - self.flatten_mapping(node) - else: - raise yaml.constructor.ConstructorError( - None, None, - u'expected a mapping node, but found %s' % node.id, - node.start_mark - ) - - mapping = OrderedDict() - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - try: - hash(key) - except TypeError as exc: - raise yaml.constructor.ConstructorError( - u'while constructing a mapping', - node.start_mark, 'found unacceptable key (%s)' % exc, - key_node.start_mark - ) - value = self.construct_object(value_node, deep=deep) - mapping[key] = value - return mapping - - # Allow bare strings to begin with %. Directives are still detected. - def check_plain(self): - plain = super(Loader, self).check_plain() - return plain or self.peek() == '%' - - -Loader.add_constructor('tag:yaml.org,2002:str', Loader._construct_unicode) -Loader.add_constructor('tag:yaml.org,2002:map', Loader.construct_yaml_map) -Loader.add_constructor('tag:yaml.org,2002:omap', Loader.construct_yaml_map) - - -def load_yaml(filename): - """Read a YAML document from a file. If the file cannot be read or - parsed, a ConfigReadError is raised. - """ - try: - with open(filename, 'rb') as f: - return yaml.load(f, Loader=Loader) - except (IOError, yaml.error.YAMLError) as exc: - raise ConfigReadError(filename, exc) - - -# YAML dumping. - -class Dumper(yaml.SafeDumper): - """A PyYAML Dumper that represents OrderedDicts as ordinary mappings - (in order, of course). - """ - # From http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py - def represent_mapping(self, tag, mapping, flow_style=None): - value = [] - node = yaml.MappingNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = False - if hasattr(mapping, 'items'): - mapping = list(mapping.items()) - for item_key, item_value in mapping: - node_key = self.represent_data(item_key) - node_value = self.represent_data(item_value) - if not (isinstance(node_key, yaml.ScalarNode) and - not node_key.style): - best_style = False - if not (isinstance(node_value, yaml.ScalarNode) and - not node_value.style): - best_style = False - value.append((node_key, node_value)) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def represent_list(self, data): - """If a list has less than 4 items, represent it in inline style - (i.e. comma separated, within square brackets). - """ - node = super(Dumper, self).represent_list(data) - length = len(data) - if self.default_flow_style is None and length < 4: - node.flow_style = True - elif self.default_flow_style is None: - node.flow_style = False - return node - - def represent_bool(self, data): - """Represent bool as 'yes' or 'no' instead of 'true' or 'false'. - """ - if data: - value = u'yes' - else: - value = u'no' - return self.represent_scalar('tag:yaml.org,2002:bool', value) - - def represent_none(self, data): - """Represent a None value with nothing instead of 'none'. - """ - return self.represent_scalar('tag:yaml.org,2002:null', '') - - -Dumper.add_representer(OrderedDict, Dumper.represent_dict) -Dumper.add_representer(bool, Dumper.represent_bool) -Dumper.add_representer(type(None), Dumper.represent_none) -Dumper.add_representer(list, Dumper.represent_list) - - -def restore_yaml_comments(data, default_data): - """Scan default_data for comments (we include empty lines in our - definition of comments) and place them before the same keys in data. - Only works with comments that are on one or more own lines, i.e. - not next to a yaml mapping. - """ - comment_map = dict() - default_lines = iter(default_data.splitlines()) - for line in default_lines: - if not line: - comment = "\n" - elif line.startswith("#"): - comment = "{0}\n".format(line) - else: - continue - while True: - line = next(default_lines) - if line and not line.startswith("#"): - break - comment += "{0}\n".format(line) - key = line.split(':')[0].strip() - comment_map[key] = comment - out_lines = iter(data.splitlines()) - out_data = "" - for line in out_lines: - key = line.split(':')[0].strip() - if key in comment_map: - out_data += comment_map[key] - out_data += "{0}\n".format(line) - return out_data - - -# Main interface. - -class Configuration(RootView): - def __init__(self, appname, modname=None, read=True): - """Create a configuration object by reading the - automatically-discovered config files for the application for a - given name. If `modname` is specified, it should be the import - name of a module whose package will be searched for a default - config file. (Otherwise, no defaults are used.) Pass `False` for - `read` to disable automatic reading of all discovered - configuration files. Use this when creating a configuration - object at module load time and then call the `read` method - later. - """ - super(Configuration, self).__init__([]) - self.appname = appname - self.modname = modname - - self._env_var = '{0}DIR'.format(self.appname.upper()) - - if read: - self.read() - - def user_config_path(self): - """Points to the location of the user configuration. - - The file may not exist. - """ - return os.path.join(self.config_dir(), CONFIG_FILENAME) - - def _add_user_source(self): - """Add the configuration options from the YAML file in the - user's configuration directory (given by `config_dir`) if it - exists. - """ - filename = self.user_config_path() - if os.path.isfile(filename): - self.add(ConfigSource(load_yaml(filename) or {}, filename)) - - def _add_default_source(self): - """Add the package's default configuration settings. This looks - for a YAML file located inside the package for the module - `modname` if it was given. - """ - if self.modname: - pkg_path = _package_path(self.modname) - if pkg_path: - filename = os.path.join(pkg_path, DEFAULT_FILENAME) - if os.path.isfile(filename): - self.add(ConfigSource(load_yaml(filename), filename, True)) - - def read(self, user=True, defaults=True): - """Find and read the files for this configuration and set them - as the sources for this configuration. To disable either - discovered user configuration files or the in-package defaults, - set `user` or `defaults` to `False`. - """ - if user: - self._add_user_source() - if defaults: - self._add_default_source() - - def config_dir(self): - """Get the path to the user configuration directory. The - directory is guaranteed to exist as a postcondition (one may be - created if none exist). - - If the application's ``...DIR`` environment variable is set, it - is used as the configuration directory. Otherwise, - platform-specific standard configuration locations are searched - for a ``config.yaml`` file. If no configuration file is found, a - fallback path is used. - """ - # If environment variable is set, use it. - if self._env_var in os.environ: - appdir = os.environ[self._env_var] - appdir = os.path.abspath(os.path.expanduser(appdir)) - if os.path.isfile(appdir): - raise ConfigError(u'{0} must be a directory'.format( - self._env_var - )) - - else: - # Search platform-specific locations. If no config file is - # found, fall back to the final directory in the list. - for confdir in config_dirs(): - appdir = os.path.join(confdir, self.appname) - if os.path.isfile(os.path.join(appdir, CONFIG_FILENAME)): - break - - # Ensure that the directory exists. - if not os.path.isdir(appdir): - os.makedirs(appdir) - return appdir - - def set_file(self, filename): - """Parses the file as YAML and inserts it into the configuration - sources with highest priority. - """ - filename = os.path.abspath(filename) - self.set(ConfigSource(load_yaml(filename), filename)) - - def dump(self, full=True, redact=False): - """Dump the Configuration object to a YAML file. - - The order of the keys is determined from the default - configuration file. All keys not in the default configuration - will be appended to the end of the file. - - :param filename: The file to dump the configuration to, or None - if the YAML string should be returned instead - :type filename: unicode - :param full: Dump settings that don't differ from the defaults - as well - :param redact: Remove sensitive information (views with the `redact` - flag set) from the output - """ - if full: - out_dict = self.flatten(redact=redact) - else: - # Exclude defaults when flattening. - sources = [s for s in self.sources if not s.default] - temp_root = RootView(sources) - temp_root.redactions = self.redactions - out_dict = temp_root.flatten(redact=redact) - - yaml_out = yaml.dump(out_dict, Dumper=Dumper, - default_flow_style=None, indent=4, - width=1000) - - # Restore comments to the YAML text. - default_source = None - for source in self.sources: - if source.default: - default_source = source - break - if default_source and default_source.filename: - with open(default_source.filename, 'rb') as fp: - default_data = fp.read() - yaml_out = restore_yaml_comments(yaml_out, - default_data.decode('utf8')) - - return yaml_out - - -class LazyConfig(Configuration): - """A Configuration at reads files on demand when it is first - accessed. This is appropriate for using as a global config object at - the module level. - """ - def __init__(self, appname, modname=None): - super(LazyConfig, self).__init__(appname, modname, False) - self._materialized = False # Have we read the files yet? - self._lazy_prefix = [] # Pre-materialization calls to set(). - self._lazy_suffix = [] # Calls to add(). - - def read(self, user=True, defaults=True): - self._materialized = True - super(LazyConfig, self).read(user, defaults) - - def resolve(self): - if not self._materialized: - # Read files and unspool buffers. - self.read() - self.sources += self._lazy_suffix - self.sources[:0] = self._lazy_prefix - return super(LazyConfig, self).resolve() - - def add(self, value): - super(LazyConfig, self).add(value) - if not self._materialized: - # Buffer additions to end. - self._lazy_suffix += self.sources - del self.sources[:] - - def set(self, value): - super(LazyConfig, self).set(value) - if not self._materialized: - # Buffer additions to beginning. - self._lazy_prefix[:0] = self.sources - del self.sources[:] - - def clear(self): - """Remove all sources from this configuration.""" - super(LazyConfig, self).clear() - self._lazy_suffix = [] - self._lazy_prefix = [] - - -# "Validated" configuration views: experimental! - - -REQUIRED = object() -"""A sentinel indicating that there is no default value and an exception -should be raised when the value is missing. -""" - - -class Template(object): - """A value template for configuration fields. - - The template works like a type and instructs Confuse about how to - interpret a deserialized YAML value. This includes type conversions, - providing a default value, and validating for errors. For example, a - filepath type might expand tildes and check that the file exists. - """ - def __init__(self, default=REQUIRED): - """Create a template with a given default value. - - If `default` is the sentinel `REQUIRED` (as it is by default), - then an error will be raised when a value is missing. Otherwise, - missing values will instead return `default`. - """ - self.default = default - - def __call__(self, view): - """Invoking a template on a view gets the view's value according - to the template. - """ - return self.value(view, self) - - def value(self, view, template=None): - """Get the value for a `ConfigView`. - - May raise a `NotFoundError` if the value is missing (and the - template requires it) or a `ConfigValueError` for invalid values. - """ - if view.exists(): - value, _ = view.first() - return self.convert(value, view) - elif self.default is REQUIRED: - # Missing required value. This is an error. - raise NotFoundError(u"{0} not found".format(view.name)) - else: - # Missing value, but not required. - return self.default - - def convert(self, value, view): - """Convert the YAML-deserialized value to a value of the desired - type. - - Subclasses should override this to provide useful conversions. - May raise a `ConfigValueError` when the configuration is wrong. - """ - # Default implementation does no conversion. - return value - - def fail(self, message, view, type_error=False): - """Raise an exception indicating that a value cannot be - accepted. - - `type_error` indicates whether the error is due to a type - mismatch rather than a malformed value. In this case, a more - specific exception is raised. - """ - exc_class = ConfigTypeError if type_error else ConfigValueError - raise exc_class( - u'{0}: {1}'.format(view.name, message) - ) - - def __repr__(self): - return '{0}({1})'.format( - type(self).__name__, - '' if self.default is REQUIRED else repr(self.default), - ) - - -class Integer(Template): - """An integer configuration value template. - """ - def convert(self, value, view): - """Check that the value is an integer. Floats are rounded. - """ - if isinstance(value, int): - return value - elif isinstance(value, float): - return int(value) - else: - self.fail(u'must be a number', view, True) - - -class Number(Template): - """A numeric type: either an integer or a floating-point number. - """ - def convert(self, value, view): - """Check that the value is an int or a float. - """ - if isinstance(value, NUMERIC_TYPES): - return value - else: - self.fail( - u'must be numeric, not {0}'.format(type(value).__name__), - view, - True - ) - - -class MappingTemplate(Template): - """A template that uses a dictionary to specify other types for the - values for a set of keys and produce a validated `AttrDict`. - """ - def __init__(self, mapping): - """Create a template according to a dict (mapping). The - mapping's values should themselves either be Types or - convertible to Types. - """ - subtemplates = {} - for key, typ in mapping.items(): - subtemplates[key] = as_template(typ) - self.subtemplates = subtemplates - - def value(self, view, template=None): - """Get a dict with the same keys as the template and values - validated according to the value types. - """ - out = AttrDict() - for key, typ in self.subtemplates.items(): - out[key] = typ.value(view[key], self) - return out - - def __repr__(self): - return 'MappingTemplate({0})'.format(repr(self.subtemplates)) - - -class String(Template): - """A string configuration value template. - """ - def __init__(self, default=REQUIRED, pattern=None): - """Create a template with the added optional `pattern` argument, - a regular expression string that the value should match. - """ - super(String, self).__init__(default) - self.pattern = pattern - if pattern: - self.regex = re.compile(pattern) - - def __repr__(self): - args = [] - - if self.default is not REQUIRED: - args.append(repr(self.default)) - - if self.pattern is not None: - args.append('pattern=' + repr(self.pattern)) - - return 'String({0})'.format(', '.join(args)) - - def convert(self, value, view): - """Check that the value is a string and matches the pattern. - """ - if isinstance(value, BASESTRING): - if self.pattern and not self.regex.match(value): - self.fail( - u"must match the pattern {0}".format(self.pattern), - view - ) - return value - else: - self.fail(u'must be a string', view, True) - - -class Choice(Template): - """A template that permits values from a sequence of choices. - """ - def __init__(self, choices): - """Create a template that validates any of the values from the - iterable `choices`. - - If `choices` is a map, then the corresponding value is emitted. - Otherwise, the value itself is emitted. - """ - self.choices = choices - - def convert(self, value, view): - """Ensure that the value is among the choices (and remap if the - choices are a mapping). - """ - if value not in self.choices: - self.fail( - u'must be one of {0}, not {1}'.format( - repr(list(self.choices)), repr(value) - ), - view - ) - - if isinstance(self.choices, collections.Mapping): - return self.choices[value] - else: - return value - - def __repr__(self): - return 'Choice({0!r})'.format(self.choices) - - -class OneOf(Template): - """A template that permits values complying to one of the given templates. - """ - def __init__(self, allowed, default=REQUIRED): - super(OneOf, self).__init__(default) - self.allowed = list(allowed) - - def __repr__(self): - args = [] - - if self.allowed is not None: - args.append('allowed=' + repr(self.allowed)) - - if self.default is not REQUIRED: - args.append(repr(self.default)) - - return 'OneOf({0})'.format(', '.join(args)) - - def value(self, view, template): - self.template = template - return super(OneOf, self).value(view, template) - - def convert(self, value, view): - """Ensure that the value follows at least one template. - """ - is_mapping = isinstance(self.template, MappingTemplate) - - for candidate in self.allowed: - try: - if is_mapping: - if isinstance(candidate, Filename) and \ - candidate.relative_to: - next_template = candidate.template_with_relatives( - view, - self.template - ) - - next_template.subtemplates[view.key] = as_template( - candidate - ) - else: - next_template = MappingTemplate({view.key: candidate}) - - return view.parent.get(next_template)[view.key] - else: - return view.get(candidate) - except ConfigTemplateError: - raise - except ConfigError: - pass - except ValueError as exc: - raise ConfigTemplateError(exc) - - self.fail( - u'must be one of {0}, not {1}'.format( - repr(self.allowed), repr(value) - ), - view - ) - - -class StrSeq(Template): - """A template for values that are lists of strings. - - Validates both actual YAML string lists and single strings. Strings - can optionally be split on whitespace. - """ - def __init__(self, split=True): - """Create a new template. - - `split` indicates whether, when the underlying value is a single - string, it should be split on whitespace. Otherwise, the - resulting value is a list containing a single string. - """ - super(StrSeq, self).__init__() - self.split = split - - def _convert_value(self, x, view): - if isinstance(x, STRING): - return x - elif isinstance(x, bytes): - return x.decode('utf-8', 'ignore') - else: - self.fail(u'must be a list of strings', view, True) - - def convert(self, value, view): - if isinstance(value, bytes): - value = value.decode('utf-8', 'ignore') - - if isinstance(value, STRING): - if self.split: - value = value.split() - else: - value = [value] - else: - try: - value = list(value) - except TypeError: - self.fail(u'must be a whitespace-separated string or a list', - view, True) - - return [self._convert_value(v, view) for v in value] - - -class Pairs(StrSeq): - """A template for ordered key-value pairs. - - This can either be given with the same syntax as for `StrSeq` (i.e. without - values), or as a list of strings and/or single-element mappings such as:: - - - key: value - - [key, value] - - key - - The result is a list of two-element tuples. If no value is provided, the - `default_value` will be returned as the second element. - """ - - def __init__(self, default_value=None): - """Create a new template. - - `default` is the dictionary value returned for items that are not - a mapping, but a single string. - """ - super(Pairs, self).__init__(split=True) - self.default_value = default_value - - def _convert_value(self, x, view): - try: - return (super(Pairs, self)._convert_value(x, view), - self.default_value) - except ConfigTypeError: - if isinstance(x, collections.Mapping): - if len(x) != 1: - self.fail(u'must be a single-element mapping', view, True) - k, v = iter_first(x.items()) - elif isinstance(x, collections.Sequence): - if len(x) != 2: - self.fail(u'must be a two-element list', view, True) - k, v = x - else: - # Is this even possible? -> Likely, if some !directive cause - # YAML to parse this to some custom type. - self.fail(u'must be a single string, mapping, or a list' - u'' + str(x), - view, True) - return (super(Pairs, self)._convert_value(k, view), - super(Pairs, self)._convert_value(v, view)) - - -class Filename(Template): - """A template that validates strings as filenames. - - Filenames are returned as absolute, tilde-free paths. - - Relative paths are relative to the template's `cwd` argument - when it is specified, then the configuration directory (see - the `config_dir` method) if they come from a file. Otherwise, - they are relative to the current working directory. This helps - attain the expected behavior when using command-line options. - """ - def __init__(self, default=REQUIRED, cwd=None, relative_to=None, - in_app_dir=False): - """`relative_to` is the name of a sibling value that is - being validated at the same time. - - `in_app_dir` indicates whether the path should be resolved - inside the application's config directory (even when the setting - does not come from a file). - """ - super(Filename, self).__init__(default) - self.cwd = cwd - self.relative_to = relative_to - self.in_app_dir = in_app_dir - - def __repr__(self): - args = [] - - if self.default is not REQUIRED: - args.append(repr(self.default)) - - if self.cwd is not None: - args.append('cwd=' + repr(self.cwd)) - - if self.relative_to is not None: - args.append('relative_to=' + repr(self.relative_to)) - - if self.in_app_dir: - args.append('in_app_dir=True') - - return 'Filename({0})'.format(', '.join(args)) - - def resolve_relative_to(self, view, template): - if not isinstance(template, (collections.Mapping, MappingTemplate)): - # disallow config.get(Filename(relative_to='foo')) - raise ConfigTemplateError( - u'relative_to may only be used when getting multiple values.' - ) - - elif self.relative_to == view.key: - raise ConfigTemplateError( - u'{0} is relative to itself'.format(view.name) - ) - - elif self.relative_to not in view.parent.keys(): - # self.relative_to is not in the config - self.fail( - ( - u'needs sibling value "{0}" to expand relative path' - ).format(self.relative_to), - view - ) - - old_template = {} - old_template.update(template.subtemplates) - - # save time by skipping MappingTemplate's init loop - next_template = MappingTemplate({}) - next_relative = self.relative_to - - # gather all the needed templates and nothing else - while next_relative is not None: - try: - # pop to avoid infinite loop because of recursive - # relative paths - rel_to_template = old_template.pop(next_relative) - except KeyError: - if next_relative in template.subtemplates: - # we encountered this config key previously - raise ConfigTemplateError(( - u'{0} and {1} are recursively relative' - ).format(view.name, self.relative_to)) - else: - raise ConfigTemplateError(( - u'missing template for {0}, needed to expand {1}\'s' + - u'relative path' - ).format(self.relative_to, view.name)) - - next_template.subtemplates[next_relative] = rel_to_template - next_relative = rel_to_template.relative_to - - return view.parent.get(next_template)[self.relative_to] - - def value(self, view, template=None): - path, source = view.first() - if not isinstance(path, BASESTRING): - self.fail( - u'must be a filename, not {0}'.format(type(path).__name__), - view, - True - ) - path = os.path.expanduser(STRING(path)) - - if not os.path.isabs(path): - if self.cwd is not None: - # relative to the template's argument - path = os.path.join(self.cwd, path) - - elif self.relative_to is not None: - path = os.path.join( - self.resolve_relative_to(view, template), - path, - ) - - elif source.filename or self.in_app_dir: - # From defaults: relative to the app's directory. - path = os.path.join(view.root().config_dir(), path) - - return os.path.abspath(path) - - -class TypeTemplate(Template): - """A simple template that checks that a value is an instance of a - desired Python type. - """ - def __init__(self, typ, default=REQUIRED): - """Create a template that checks that the value is an instance - of `typ`. - """ - super(TypeTemplate, self).__init__(default) - self.typ = typ - - def convert(self, value, view): - if not isinstance(value, self.typ): - self.fail( - u'must be a {0}, not {1}'.format( - self.typ.__name__, - type(value).__name__, - ), - view, - True - ) - return value - - -class AttrDict(dict): - """A `dict` subclass that can be accessed via attributes (dot - notation) for convenience. - """ - def __getattr__(self, key): - if key in self: - return self[key] - else: - raise AttributeError(key) - - -def as_template(value): - """Convert a simple "shorthand" Python value to a `Template`. - """ - if isinstance(value, Template): - # If it's already a Template, pass it through. - return value - elif isinstance(value, collections.Mapping): - # Dictionaries work as templates. - return MappingTemplate(value) - elif value is int: - return Integer() - elif isinstance(value, int): - return Integer(value) - elif isinstance(value, type) and issubclass(value, BASESTRING): - return String() - elif isinstance(value, BASESTRING): - return String(value) - elif isinstance(value, set): - # convert to list to avoid hash related problems - return Choice(list(value)) - elif isinstance(value, list): - return OneOf(value) - elif value is float: - return Number() - elif value is None: - return Template() - elif value is dict: - return TypeTemplate(collections.Mapping) - elif value is list: - return TypeTemplate(collections.Sequence) - elif isinstance(value, type): - return TypeTemplate(value) - else: - raise ValueError(u'cannot convert to template: {0!r}'.format(value)) +# Cleanup namespace. +del key, value, warnings, confuse diff --git a/libs/common/beets/util/enumeration.py b/libs/common/beets/util/enumeration.py index 3e946718..e49f6fdd 100644 --- a/libs/common/beets/util/enumeration.py +++ b/libs/common/beets/util/enumeration.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function from enum import Enum diff --git a/libs/common/beets/util/functemplate.py b/libs/common/beets/util/functemplate.py index 0e13db4a..289a436d 100644 --- a/libs/common/beets/util/functemplate.py +++ b/libs/common/beets/util/functemplate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -27,30 +26,30 @@ This is sort of like a tiny, horrible degeneration of a real templating engine like Jinja2 or Mustache. """ -from __future__ import division, absolute_import, print_function import re import ast import dis import types import sys -import six +import functools -SYMBOL_DELIM = u'$' -FUNC_DELIM = u'%' -GROUP_OPEN = u'{' -GROUP_CLOSE = u'}' -ARG_SEP = u',' -ESCAPE_CHAR = u'$' +SYMBOL_DELIM = '$' +FUNC_DELIM = '%' +GROUP_OPEN = '{' +GROUP_CLOSE = '}' +ARG_SEP = ',' +ESCAPE_CHAR = '$' VARIABLE_PREFIX = '__var_' FUNCTION_PREFIX = '__func_' -class Environment(object): +class Environment: """Contains the values and functions to be substituted into a template. """ + def __init__(self, values, functions): self.values = values self.functions = functions @@ -72,15 +71,7 @@ def ex_literal(val): """An int, float, long, bool, string, or None literal with the given value. """ - if val is None: - return ast.Name('None', ast.Load()) - elif isinstance(val, six.integer_types): - return ast.Num(val) - elif isinstance(val, bool): - return ast.Name(bytes(val), ast.Load()) - elif isinstance(val, six.string_types): - return ast.Str(val) - raise TypeError(u'no literal for {0}'.format(type(val))) + return ast.Constant(val) def ex_varassign(name, expr): @@ -97,7 +88,7 @@ def ex_call(func, args): function may be an expression or the name of a function. Each argument may be an expression or a value to be used as a literal. """ - if isinstance(func, six.string_types): + if isinstance(func, str): func = ex_rvalue(func) args = list(args) @@ -105,10 +96,7 @@ def ex_call(func, args): if not isinstance(args[i], ast.expr): args[i] = ex_literal(args[i]) - if sys.version_info[:2] < (3, 5): - return ast.Call(func, args, [], None, None) - else: - return ast.Call(func, args, []) + return ast.Call(func, args, []) def compile_func(arg_names, statements, name='_the_func', debug=False): @@ -116,32 +104,30 @@ def compile_func(arg_names, statements, name='_the_func', debug=False): the resulting Python function. If `debug`, then print out the bytecode of the compiled function. """ - if six.PY2: - func_def = ast.FunctionDef( - name=name.encode('utf-8'), - args=ast.arguments( - args=[ast.Name(n, ast.Param()) for n in arg_names], - vararg=None, - kwarg=None, - defaults=[ex_literal(None) for _ in arg_names], - ), - body=statements, - decorator_list=[], - ) - else: - func_def = ast.FunctionDef( - name=name, - args=ast.arguments( - args=[ast.arg(arg=n, annotation=None) for n in arg_names], - kwonlyargs=[], - kw_defaults=[], - defaults=[ex_literal(None) for _ in arg_names], - ), - body=statements, - decorator_list=[], - ) + args_fields = { + 'args': [ast.arg(arg=n, annotation=None) for n in arg_names], + 'kwonlyargs': [], + 'kw_defaults': [], + 'defaults': [ex_literal(None) for _ in arg_names], + } + if 'posonlyargs' in ast.arguments._fields: # Added in Python 3.8. + args_fields['posonlyargs'] = [] + args = ast.arguments(**args_fields) + + func_def = ast.FunctionDef( + name=name, + args=args, + body=statements, + decorator_list=[], + ) + + # The ast.Module signature changed in 3.8 to accept a list of types to + # ignore. + if sys.version_info >= (3, 8): + mod = ast.Module([func_def], []) + else: + mod = ast.Module([func_def]) - mod = ast.Module([func_def]) ast.fix_missing_locations(mod) prog = compile(mod, '', 'exec') @@ -160,14 +146,15 @@ def compile_func(arg_names, statements, name='_the_func', debug=False): # AST nodes for the template language. -class Symbol(object): +class Symbol: """A variable-substitution symbol in a template.""" + def __init__(self, ident, original): self.ident = ident self.original = original def __repr__(self): - return u'Symbol(%s)' % repr(self.ident) + return 'Symbol(%s)' % repr(self.ident) def evaluate(self, env): """Evaluate the symbol in the environment, returning a Unicode @@ -182,24 +169,22 @@ class Symbol(object): def translate(self): """Compile the variable lookup.""" - if six.PY2: - ident = self.ident.encode('utf-8') - else: - ident = self.ident + ident = self.ident expr = ex_rvalue(VARIABLE_PREFIX + ident) - return [expr], set([ident]), set() + return [expr], {ident}, set() -class Call(object): +class Call: """A function call in a template.""" + def __init__(self, ident, args, original): self.ident = ident self.args = args self.original = original def __repr__(self): - return u'Call(%s, %s, %s)' % (repr(self.ident), repr(self.args), - repr(self.original)) + return 'Call({}, {}, {})'.format(repr(self.ident), repr(self.args), + repr(self.original)) def evaluate(self, env): """Evaluate the function call in the environment, returning a @@ -212,19 +197,15 @@ class Call(object): except Exception as exc: # Function raised exception! Maybe inlining the name of # the exception will help debug. - return u'<%s>' % six.text_type(exc) - return six.text_type(out) + return '<%s>' % str(exc) + return str(out) else: return self.original def translate(self): """Compile the function call.""" varnames = set() - if six.PY2: - ident = self.ident.encode('utf-8') - else: - ident = self.ident - funcnames = set([ident]) + funcnames = {self.ident} arg_exprs = [] for arg in self.args: @@ -235,32 +216,33 @@ class Call(object): # Create a subexpression that joins the result components of # the arguments. arg_exprs.append(ex_call( - ast.Attribute(ex_literal(u''), 'join', ast.Load()), + ast.Attribute(ex_literal(''), 'join', ast.Load()), [ex_call( 'map', [ - ex_rvalue(six.text_type.__name__), + ex_rvalue(str.__name__), ast.List(subexprs, ast.Load()), ] )], )) subexpr_call = ex_call( - FUNCTION_PREFIX + ident, + FUNCTION_PREFIX + self.ident, arg_exprs ) return [subexpr_call], varnames, funcnames -class Expression(object): +class Expression: """Top-level template construct: contains a list of text blobs, Symbols, and Calls. """ + def __init__(self, parts): self.parts = parts def __repr__(self): - return u'Expression(%s)' % (repr(self.parts)) + return 'Expression(%s)' % (repr(self.parts)) def evaluate(self, env): """Evaluate the entire expression in the environment, returning @@ -268,11 +250,11 @@ class Expression(object): """ out = [] for part in self.parts: - if isinstance(part, six.string_types): + if isinstance(part, str): out.append(part) else: out.append(part.evaluate(env)) - return u''.join(map(six.text_type, out)) + return ''.join(map(str, out)) def translate(self): """Compile the expression to a list of Python AST expressions, a @@ -282,7 +264,7 @@ class Expression(object): varnames = set() funcnames = set() for part in self.parts: - if isinstance(part, six.string_types): + if isinstance(part, str): expressions.append(ex_literal(part)) else: e, v, f = part.translate() @@ -298,7 +280,7 @@ class ParseError(Exception): pass -class Parser(object): +class Parser: """Parses a template expression string. Instantiate the class with the template source and call ``parse_expression``. The ``pos`` field will indicate the character after the expression finished and @@ -311,6 +293,7 @@ class Parser(object): replaced with a real, accepted parsing technique (PEG, parser generator, etc.). """ + def __init__(self, string, in_argument=False): """ Create a new parser. :param in_arguments: boolean that indicates the parser is to be @@ -326,7 +309,7 @@ class Parser(object): special_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_OPEN, GROUP_CLOSE, ESCAPE_CHAR) special_char_re = re.compile(r'[%s]|\Z' % - u''.join(re.escape(c) for c in special_chars)) + ''.join(re.escape(c) for c in special_chars)) escapable_chars = (SYMBOL_DELIM, FUNC_DELIM, GROUP_CLOSE, ARG_SEP) terminator_chars = (GROUP_CLOSE,) @@ -343,7 +326,7 @@ class Parser(object): if self.in_argument: extra_special_chars = (ARG_SEP,) special_char_re = re.compile( - r'[%s]|\Z' % u''.join( + r'[%s]|\Z' % ''.join( re.escape(c) for c in self.special_chars + extra_special_chars ) @@ -387,7 +370,7 @@ class Parser(object): # Shift all characters collected so far into a single string. if text_parts: - self.parts.append(u''.join(text_parts)) + self.parts.append(''.join(text_parts)) text_parts = [] if char == SYMBOL_DELIM: @@ -409,7 +392,7 @@ class Parser(object): # If any parsed characters remain, shift them into a string. if text_parts: - self.parts.append(u''.join(text_parts)) + self.parts.append(''.join(text_parts)) def parse_symbol(self): """Parse a variable reference (like ``$foo`` or ``${foo}``) @@ -547,11 +530,27 @@ def _parse(template): return Expression(parts) -# External interface. +def cached(func): + """Like the `functools.lru_cache` decorator, but works (as a no-op) + on Python < 3.2. + """ + if hasattr(functools, 'lru_cache'): + return functools.lru_cache(maxsize=128)(func) + else: + # Do nothing when lru_cache is not available. + return func -class Template(object): + +@cached +def template(fmt): + return Template(fmt) + + +# External interface. +class Template: """A string template, including text, Symbols, and Calls. """ + def __init__(self, template): self.expr = _parse(template) self.original = template @@ -600,7 +599,7 @@ class Template(object): for funcname in funcnames: args[FUNCTION_PREFIX + funcname] = functions[funcname] parts = func(**args) - return u''.join(parts) + return ''.join(parts) return wrapper_func @@ -609,9 +608,9 @@ class Template(object): if __name__ == '__main__': import timeit - _tmpl = Template(u'foo $bar %baz{foozle $bar barzle} $bar') + _tmpl = Template('foo $bar %baz{foozle $bar barzle} $bar') _vars = {'bar': 'qux'} - _funcs = {'baz': six.text_type.upper} + _funcs = {'baz': str.upper} interp_time = timeit.timeit('_tmpl.interpret(_vars, _funcs)', 'from __main__ import _tmpl, _vars, _funcs', number=10000) @@ -620,4 +619,4 @@ if __name__ == '__main__': 'from __main__ import _tmpl, _vars, _funcs', number=10000) print(comp_time) - print(u'Speedup:', interp_time / comp_time) + print('Speedup:', interp_time / comp_time) diff --git a/libs/common/beets/util/hidden.py b/libs/common/beets/util/hidden.py index ed97f2bf..881de1ac 100644 --- a/libs/common/beets/util/hidden.py +++ b/libs/common/beets/util/hidden.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -14,7 +13,6 @@ # included in all copies or substantial portions of the Software. """Simple library to work out if a file is hidden on different platforms.""" -from __future__ import division, absolute_import, print_function import os import stat diff --git a/libs/common/beets/util/pipeline.py b/libs/common/beets/util/pipeline.py index 39bc7152..d338cb51 100644 --- a/libs/common/beets/util/pipeline.py +++ b/libs/common/beets/util/pipeline.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -32,12 +31,10 @@ To do so, pass an iterable of coroutines to the Pipeline constructor in place of any single coroutine. """ -from __future__ import division, absolute_import, print_function -from six.moves import queue +import queue from threading import Thread, Lock import sys -import six BUBBLE = '__PIPELINE_BUBBLE__' POISON = '__PIPELINE_POISON__' @@ -91,6 +88,7 @@ class CountedQueue(queue.Queue): still feeding into it. The queue is poisoned when all threads are finished with the queue. """ + def __init__(self, maxsize=0): queue.Queue.__init__(self, maxsize) self.nthreads = 0 @@ -135,10 +133,11 @@ class CountedQueue(queue.Queue): _invalidate_queue(self, POISON, False) -class MultiMessage(object): +class MultiMessage: """A message yielded by a pipeline stage encapsulating multiple values to be sent to the next stage. """ + def __init__(self, messages): self.messages = messages @@ -210,8 +209,9 @@ def _allmsgs(obj): class PipelineThread(Thread): """Abstract base class for pipeline-stage threads.""" + def __init__(self, all_threads): - super(PipelineThread, self).__init__() + super().__init__() self.abort_lock = Lock() self.abort_flag = False self.all_threads = all_threads @@ -241,15 +241,13 @@ class FirstPipelineThread(PipelineThread): """The thread running the first stage in a parallel pipeline setup. The coroutine should just be a generator. """ + def __init__(self, coro, out_queue, all_threads): - super(FirstPipelineThread, self).__init__(all_threads) + super().__init__(all_threads) self.coro = coro self.out_queue = out_queue self.out_queue.acquire() - self.abort_lock = Lock() - self.abort_flag = False - def run(self): try: while True: @@ -282,8 +280,9 @@ class MiddlePipelineThread(PipelineThread): """A thread running any stage in the pipeline except the first or last. """ + def __init__(self, coro, in_queue, out_queue, all_threads): - super(MiddlePipelineThread, self).__init__(all_threads) + super().__init__(all_threads) self.coro = coro self.in_queue = in_queue self.out_queue = out_queue @@ -330,8 +329,9 @@ class LastPipelineThread(PipelineThread): """A thread running the last stage in a pipeline. The coroutine should yield nothing. """ + def __init__(self, coro, in_queue, all_threads): - super(LastPipelineThread, self).__init__(all_threads) + super().__init__(all_threads) self.coro = coro self.in_queue = in_queue @@ -362,17 +362,18 @@ class LastPipelineThread(PipelineThread): return -class Pipeline(object): +class Pipeline: """Represents a staged pattern of work. Each stage in the pipeline is a coroutine that receives messages from the previous stage and yields messages to be sent to the next stage. """ + def __init__(self, stages): """Makes a new pipeline from a list of coroutines. There must be at least two stages. """ if len(stages) < 2: - raise ValueError(u'pipeline must have at least two stages') + raise ValueError('pipeline must have at least two stages') self.stages = [] for stage in stages: if isinstance(stage, (list, tuple)): @@ -442,7 +443,7 @@ class Pipeline(object): exc_info = thread.exc_info if exc_info: # Make the exception appear as it was raised originally. - six.reraise(exc_info[0], exc_info[1], exc_info[2]) + raise exc_info[1].with_traceback(exc_info[2]) def pull(self): """Yield elements from the end of the pipeline. Runs the stages @@ -469,6 +470,7 @@ class Pipeline(object): for msg in msgs: yield msg + # Smoke test. if __name__ == '__main__': import time @@ -477,14 +479,14 @@ if __name__ == '__main__': # in parallel. def produce(): for i in range(5): - print(u'generating %i' % i) + print('generating %i' % i) time.sleep(1) yield i def work(): num = yield while True: - print(u'processing %i' % num) + print('processing %i' % num) time.sleep(2) num = yield num * 2 @@ -492,7 +494,7 @@ if __name__ == '__main__': while True: num = yield time.sleep(1) - print(u'received %i' % num) + print('received %i' % num) ts_start = time.time() Pipeline([produce(), work(), consume()]).run_sequential() @@ -501,22 +503,22 @@ if __name__ == '__main__': ts_par = time.time() Pipeline([produce(), (work(), work()), consume()]).run_parallel() ts_end = time.time() - print(u'Sequential time:', ts_seq - ts_start) - print(u'Parallel time:', ts_par - ts_seq) - print(u'Multiply-parallel time:', ts_end - ts_par) + print('Sequential time:', ts_seq - ts_start) + print('Parallel time:', ts_par - ts_seq) + print('Multiply-parallel time:', ts_end - ts_par) print() # Test a pipeline that raises an exception. def exc_produce(): for i in range(10): - print(u'generating %i' % i) + print('generating %i' % i) time.sleep(1) yield i def exc_work(): num = yield while True: - print(u'processing %i' % num) + print('processing %i' % num) time.sleep(3) if num == 3: raise Exception() @@ -525,6 +527,6 @@ if __name__ == '__main__': def exc_consume(): while True: num = yield - print(u'received %i' % num) + print('received %i' % num) Pipeline([exc_produce(), exc_work(), exc_consume()]).run_parallel(1) diff --git a/libs/common/beets/vfs.py b/libs/common/beets/vfs.py index 7f9a049e..aef69650 100644 --- a/libs/common/beets/vfs.py +++ b/libs/common/beets/vfs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,7 +15,6 @@ """A simple utility for constructing filesystem-like trees from beets libraries. """ -from __future__ import division, absolute_import, print_function from collections import namedtuple from beets import util diff --git a/libs/common/beetsplug/__init__.py b/libs/common/beetsplug/__init__.py index febeb66f..da248491 100644 --- a/libs/common/beetsplug/__init__.py +++ b/libs/common/beetsplug/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,7 +14,6 @@ """A namespace package for beets plugins.""" -from __future__ import division, absolute_import, print_function # Make this a namespace package. from pkgutil import extend_path diff --git a/libs/common/beetsplug/absubmit.py b/libs/common/beetsplug/absubmit.py index 0c288b9d..d1ea692f 100644 --- a/libs/common/beetsplug/absubmit.py +++ b/libs/common/beetsplug/absubmit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Pieter Mulder. # @@ -16,7 +15,6 @@ """Calculate acoustic information and submit to AcousticBrainz. """ -from __future__ import division, absolute_import, print_function import errno import hashlib @@ -32,6 +30,9 @@ from beets import plugins from beets import util from beets import ui +# We use this field to check whether AcousticBrainz info is present. +PROBE_FIELD = 'mood_acoustic' + class ABSubmitError(Exception): """Raised when failing to analyse file with extractor.""" @@ -43,19 +44,23 @@ def call(args): Raise a AnalysisABSubmitError on failure. """ try: - return util.command_output(args) + return util.command_output(args).stdout except subprocess.CalledProcessError as e: raise ABSubmitError( - u'{0} exited with status {1}'.format(args[0], e.returncode) + '{} exited with status {}'.format(args[0], e.returncode) ) class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): def __init__(self): - super(AcousticBrainzSubmitPlugin, self).__init__() + super().__init__() - self.config.add({'extractor': u''}) + self.config.add({ + 'extractor': '', + 'force': False, + 'pretend': False + }) self.extractor = self.config['extractor'].as_str() if self.extractor: @@ -63,7 +68,7 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): # Expicit path to extractor if not os.path.isfile(self.extractor): raise ui.UserError( - u'Extractor command does not exist: {0}.'. + 'Extractor command does not exist: {0}.'. format(self.extractor) ) else: @@ -73,8 +78,8 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): call([self.extractor]) except OSError: raise ui.UserError( - u'No extractor command found: please install the ' - u'extractor binary from http://acousticbrainz.org/download' + 'No extractor command found: please install the extractor' + ' binary from https://acousticbrainz.org/download' ) except ABSubmitError: # Extractor found, will exit with an error if not called with @@ -96,7 +101,18 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): def commands(self): cmd = ui.Subcommand( 'absubmit', - help=u'calculate and submit AcousticBrainz analysis' + help='calculate and submit AcousticBrainz analysis' + ) + cmd.parser.add_option( + '-f', '--force', dest='force_refetch', + action='store_true', default=False, + help='re-download data when already present' + ) + cmd.parser.add_option( + '-p', '--pretend', dest='pretend_fetch', + action='store_true', default=False, + help='pretend to perform action, but show \ +only files which would be processed' ) cmd.func = self.command return [cmd] @@ -104,17 +120,30 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): def command(self, lib, opts, args): # Get items from arguments items = lib.items(ui.decargs(args)) - for item in items: - analysis = self._get_analysis(item) - if analysis: - self._submit_data(item, analysis) + self.opts = opts + util.par_map(self.analyze_submit, items) + + def analyze_submit(self, item): + analysis = self._get_analysis(item) + if analysis: + self._submit_data(item, analysis) def _get_analysis(self, item): mbid = item['mb_trackid'] - # If file has no mbid skip it. + + # Avoid re-analyzing files that already have AB data. + if not self.opts.force_refetch and not self.config['force']: + if item.get(PROBE_FIELD): + return None + + # If file has no MBID, skip it. if not mbid: - self._log.info(u'Not analysing {}, missing ' - u'musicbrainz track id.', item) + self._log.info('Not analysing {}, missing ' + 'musicbrainz track id.', item) + return None + + if self.opts.pretend_fetch or self.config['pretend']: + self._log.info('pretend action - extract item: {}', item) return None # Temporary file to save extractor output to, extractor only works @@ -129,11 +158,11 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): call([self.extractor, util.syspath(item.path), filename]) except ABSubmitError as e: self._log.warning( - u'Failed to analyse {item} for AcousticBrainz: {error}', + 'Failed to analyse {item} for AcousticBrainz: {error}', item=item, error=e ) return None - with open(filename, 'rb') as tmp_file: + with open(filename) as tmp_file: analysis = json.load(tmp_file) # Add the hash to the output. analysis['metadata']['version']['essentia_build_sha'] = \ @@ -157,11 +186,11 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin): try: message = response.json()['message'] except (ValueError, KeyError) as e: - message = u'unable to get error message: {}'.format(e) + message = f'unable to get error message: {e}' self._log.error( - u'Failed to submit AcousticBrainz analysis of {item}: ' - u'{message}).', item=item, message=message + 'Failed to submit AcousticBrainz analysis of {item}: ' + '{message}).', item=item, message=message ) else: - self._log.debug(u'Successfully submitted AcousticBrainz analysis ' - u'for {}.', item) + self._log.debug('Successfully submitted AcousticBrainz analysis ' + 'for {}.', item) diff --git a/libs/common/beetsplug/acousticbrainz.py b/libs/common/beetsplug/acousticbrainz.py index f4960c30..eabc5849 100644 --- a/libs/common/beetsplug/acousticbrainz.py +++ b/libs/common/beetsplug/acousticbrainz.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2015-2016, Ohm Patel. # @@ -15,12 +14,13 @@ """Fetch various AcousticBrainz metadata using MBID. """ -from __future__ import division, absolute_import, print_function + +from collections import defaultdict import requests -from collections import defaultdict from beets import plugins, ui +from beets.dbcore import types ACOUSTIC_BASE = "https://acousticbrainz.org/" LEVELS = ["/low-level", "/high-level"] @@ -72,6 +72,9 @@ ABSCHEME = { 'sad': 'mood_sad' } }, + 'moods_mirex': { + 'value': 'moods_mirex' + }, 'ismir04_rhythm': { 'value': 'rhythm' }, @@ -80,6 +83,9 @@ ABSCHEME = { 'tonal': 'tonal' } }, + 'timbre': { + 'value': 'timbre' + }, 'voice_instrumental': { 'value': 'voice_instrumental' }, @@ -104,8 +110,33 @@ ABSCHEME = { class AcousticPlugin(plugins.BeetsPlugin): + item_types = { + 'average_loudness': types.Float(6), + 'chords_changes_rate': types.Float(6), + 'chords_key': types.STRING, + 'chords_number_rate': types.Float(6), + 'chords_scale': types.STRING, + 'danceable': types.Float(6), + 'gender': types.STRING, + 'genre_rosamerica': types.STRING, + 'initial_key': types.STRING, + 'key_strength': types.Float(6), + 'mood_acoustic': types.Float(6), + 'mood_aggressive': types.Float(6), + 'mood_electronic': types.Float(6), + 'mood_happy': types.Float(6), + 'mood_party': types.Float(6), + 'mood_relaxed': types.Float(6), + 'mood_sad': types.Float(6), + 'moods_mirex': types.STRING, + 'rhythm': types.Float(6), + 'timbre': types.STRING, + 'tonal': types.Float(6), + 'voice_instrumental': types.STRING, + } + def __init__(self): - super(AcousticPlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, @@ -119,11 +150,11 @@ class AcousticPlugin(plugins.BeetsPlugin): def commands(self): cmd = ui.Subcommand('acousticbrainz', - help=u"fetch metadata from AcousticBrainz") + help="fetch metadata from AcousticBrainz") cmd.parser.add_option( - u'-f', u'--force', dest='force_refetch', + '-f', '--force', dest='force_refetch', action='store_true', default=False, - help=u're-download data when already present' + help='re-download data when already present' ) def func(lib, opts, args): @@ -142,22 +173,22 @@ class AcousticPlugin(plugins.BeetsPlugin): def _get_data(self, mbid): data = {} for url in _generate_urls(mbid): - self._log.debug(u'fetching URL: {}', url) + self._log.debug('fetching URL: {}', url) try: res = requests.get(url) except requests.RequestException as exc: - self._log.info(u'request error: {}', exc) + self._log.info('request error: {}', exc) return {} if res.status_code == 404: - self._log.info(u'recording ID {} not found', mbid) + self._log.info('recording ID {} not found', mbid) return {} try: data.update(res.json()) except ValueError: - self._log.debug(u'Invalid Response: {}', res.text) + self._log.debug('Invalid Response: {}', res.text) return {} return data @@ -172,28 +203,28 @@ class AcousticPlugin(plugins.BeetsPlugin): # representative field name to check for previously fetched # data. if not force: - mood_str = item.get('mood_acoustic', u'') + mood_str = item.get('mood_acoustic', '') if mood_str: - self._log.info(u'data already present for: {}', item) + self._log.info('data already present for: {}', item) continue # We can only fetch data for tracks with MBIDs. if not item.mb_trackid: continue - self._log.info(u'getting data for: {}', item) + self._log.info('getting data for: {}', item) data = self._get_data(item.mb_trackid) if data: for attr, val in self._map_data_to_scheme(data, ABSCHEME): if not tags or attr in tags: - self._log.debug(u'attribute {} of {} set to {}', + self._log.debug('attribute {} of {} set to {}', attr, item, val) setattr(item, attr, val) else: - self._log.debug(u'skipping attribute {} of {}' - u' (value {}) due to config', + self._log.debug('skipping attribute {} of {}' + ' (value {}) due to config', attr, item, val) @@ -255,10 +286,9 @@ class AcousticPlugin(plugins.BeetsPlugin): # The recursive traversal. composites = defaultdict(list) - for attr, val in self._data_to_scheme_child(data, - scheme, - composites): - yield attr, val + yield from self._data_to_scheme_child(data, + scheme, + composites) # When composites has been populated, yield the composite attributes # by joining their parts. @@ -278,10 +308,9 @@ class AcousticPlugin(plugins.BeetsPlugin): for k, v in subscheme.items(): if k in subdata: if type(v) == dict: - for attr, val in self._data_to_scheme_child(subdata[k], - v, - composites): - yield attr, val + yield from self._data_to_scheme_child(subdata[k], + v, + composites) elif type(v) == tuple: composite_attribute, part_number = v attribute_parts = composites[composite_attribute] @@ -292,10 +321,10 @@ class AcousticPlugin(plugins.BeetsPlugin): else: yield v, subdata[k] else: - self._log.warning(u'Acousticbrainz did not provide info' - u'about {}', k) - self._log.debug(u'Data {} could not be mapped to scheme {} ' - u'because key {} was not found', subdata, v, k) + self._log.warning('Acousticbrainz did not provide info' + 'about {}', k) + self._log.debug('Data {} could not be mapped to scheme {} ' + 'because key {} was not found', subdata, v, k) def _generate_urls(mbid): diff --git a/libs/common/beetsplug/albumtypes.py b/libs/common/beetsplug/albumtypes.py new file mode 100644 index 00000000..47f8dc64 --- /dev/null +++ b/libs/common/beetsplug/albumtypes.py @@ -0,0 +1,65 @@ +# This file is part of beets. +# Copyright 2021, Edgars Supe. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Adds an album template field for formatted album types.""" + + +from beets.autotag.mb import VARIOUS_ARTISTS_ID +from beets.library import Album +from beets.plugins import BeetsPlugin + + +class AlbumTypesPlugin(BeetsPlugin): + """Adds an album template field for formatted album types.""" + + def __init__(self): + """Init AlbumTypesPlugin.""" + super().__init__() + self.album_template_fields['atypes'] = self._atypes + self.config.add({ + 'types': [ + ('ep', 'EP'), + ('single', 'Single'), + ('soundtrack', 'OST'), + ('live', 'Live'), + ('compilation', 'Anthology'), + ('remix', 'Remix') + ], + 'ignore_va': ['compilation'], + 'bracket': '[]' + }) + + def _atypes(self, item: Album): + """Returns a formatted string based on album's types.""" + types = self.config['types'].as_pairs() + ignore_va = self.config['ignore_va'].as_str_seq() + bracket = self.config['bracket'].as_str() + + # Assign a left and right bracket or leave blank if argument is empty. + if len(bracket) == 2: + bracket_l = bracket[0] + bracket_r = bracket[1] + else: + bracket_l = '' + bracket_r = '' + + res = '' + albumtypes = item.albumtypes.split('; ') + is_va = item.mb_albumartistid == VARIOUS_ARTISTS_ID + for type in types: + if type[0] in albumtypes and type[1]: + if not is_va or (type[0] not in ignore_va and is_va): + res += f'{bracket_l}{type[1]}{bracket_r}' + + return res diff --git a/libs/common/beetsplug/aura.py b/libs/common/beetsplug/aura.py new file mode 100644 index 00000000..f4ae5527 --- /dev/null +++ b/libs/common/beetsplug/aura.py @@ -0,0 +1,984 @@ +# This file is part of beets. +# Copyright 2020, Callum Brown. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""An AURA server using Flask.""" + + +from mimetypes import guess_type +import re +import os.path +from os.path import isfile, getsize + +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand, _open_library +from beets import config +from beets.util import py3_path +from beets.library import Item, Album +from beets.dbcore.query import ( + MatchQuery, + NotQuery, + RegexpQuery, + AndQuery, + FixedFieldSort, + SlowFieldSort, + MultipleSort, +) + +from flask import ( + Blueprint, + Flask, + current_app, + send_file, + make_response, + request, +) + + +# Constants + +# AURA server information +# TODO: Add version information +SERVER_INFO = { + "aura-version": "0", + "server": "beets-aura", + "server-version": "0.1", + "auth-required": False, + "features": ["albums", "artists", "images"], +} + +# Maps AURA Track attribute to beets Item attribute +TRACK_ATTR_MAP = { + # Required + "title": "title", + "artist": "artist", + # Optional + "album": "album", + "track": "track", # Track number on album + "tracktotal": "tracktotal", + "disc": "disc", + "disctotal": "disctotal", + "year": "year", + "month": "month", + "day": "day", + "bpm": "bpm", + "genre": "genre", + "recording-mbid": "mb_trackid", # beets trackid is MB recording + "track-mbid": "mb_releasetrackid", + "composer": "composer", + "albumartist": "albumartist", + "comments": "comments", + # Optional for Audio Metadata + # TODO: Support the mimetype attribute, format != mime type + # "mimetype": track.format, + "duration": "length", + "framerate": "samplerate", + # I don't think beets has a framecount field + # "framecount": ???, + "channels": "channels", + "bitrate": "bitrate", + "bitdepth": "bitdepth", + "size": "filesize", +} + +# Maps AURA Album attribute to beets Album attribute +ALBUM_ATTR_MAP = { + # Required + "title": "album", + "artist": "albumartist", + # Optional + "tracktotal": "albumtotal", + "disctotal": "disctotal", + "year": "year", + "month": "month", + "day": "day", + "genre": "genre", + "release-mbid": "mb_albumid", + "release-group-mbid": "mb_releasegroupid", +} + +# Maps AURA Artist attribute to beets Item field +# Artists are not first-class in beets, so information is extracted from +# beets Items. +ARTIST_ATTR_MAP = { + # Required + "name": "artist", + # Optional + "artist-mbid": "mb_artistid", +} + + +class AURADocument: + """Base class for building AURA documents.""" + + @staticmethod + def error(status, title, detail): + """Make a response for an error following the JSON:API spec. + + Args: + status: An HTTP status code string, e.g. "404 Not Found". + title: A short, human-readable summary of the problem. + detail: A human-readable explanation specific to this + occurrence of the problem. + """ + document = { + "errors": [{"status": status, "title": title, "detail": detail}] + } + return make_response(document, status) + + def translate_filters(self): + """Translate filters from request arguments to a beets Query.""" + # The format of each filter key in the request parameter is: + # filter[]. This regex extracts . + pattern = re.compile(r"filter\[(?P[a-zA-Z0-9_-]+)\]") + queries = [] + for key, value in request.args.items(): + match = pattern.match(key) + if match: + # Extract attribute name from key + aura_attr = match.group("attribute") + # Get the beets version of the attribute name + beets_attr = self.attribute_map.get(aura_attr, aura_attr) + converter = self.get_attribute_converter(beets_attr) + value = converter(value) + # Add exact match query to list + # Use a slow query so it works with all fields + queries.append(MatchQuery(beets_attr, value, fast=False)) + # NOTE: AURA doesn't officially support multiple queries + return AndQuery(queries) + + def translate_sorts(self, sort_arg): + """Translate an AURA sort parameter into a beets Sort. + + Args: + sort_arg: The value of the 'sort' query parameter; a comma + separated list of fields to sort by, in order. + E.g. "-year,title". + """ + # Change HTTP query parameter to a list + aura_sorts = sort_arg.strip(",").split(",") + sorts = [] + for aura_attr in aura_sorts: + if aura_attr[0] == "-": + ascending = False + # Remove leading "-" + aura_attr = aura_attr[1:] + else: + # JSON:API default + ascending = True + # Get the beets version of the attribute name + beets_attr = self.attribute_map.get(aura_attr, aura_attr) + # Use slow sort so it works with all fields (inc. computed) + sorts.append(SlowFieldSort(beets_attr, ascending=ascending)) + return MultipleSort(sorts) + + def paginate(self, collection): + """Get a page of the collection and the URL to the next page. + + Args: + collection: The raw data from which resource objects can be + built. Could be an sqlite3.Cursor object (tracks and + albums) or a list of strings (artists). + """ + # Pages start from zero + page = request.args.get("page", 0, int) + # Use page limit defined in config by default. + default_limit = config["aura"]["page_limit"].get(int) + limit = request.args.get("limit", default_limit, int) + # start = offset of first item to return + start = page * limit + # end = offset of last item + 1 + end = start + limit + if end > len(collection): + end = len(collection) + next_url = None + else: + # Not the last page so work out links.next url + if not request.args: + # No existing arguments, so current page is 0 + next_url = request.url + "?page=1" + elif not request.args.get("page", None): + # No existing page argument, so add one to the end + next_url = request.url + "&page=1" + else: + # Increment page token by 1 + next_url = request.url.replace( + f"page={page}", "page={}".format(page + 1) + ) + # Get only the items in the page range + data = [self.resource_object(collection[i]) for i in range(start, end)] + return data, next_url + + def get_included(self, data, include_str): + """Build a list of resource objects for inclusion. + + Args: + data: An array of dicts in the form of resource objects. + include_str: A comma separated list of resource types to + include. E.g. "tracks,images". + """ + # Change HTTP query parameter to a list + to_include = include_str.strip(",").split(",") + # Build a list of unique type and id combinations + # For each resource object in the primary data, iterate over it's + # relationships. If a relationship matches one of the types + # requested for inclusion (e.g. "albums") then add each type-id pair + # under the "data" key to unique_identifiers, checking first that + # it has not already been added. This ensures that no resources are + # included more than once. + unique_identifiers = [] + for res_obj in data: + for rel_name, rel_obj in res_obj["relationships"].items(): + if rel_name in to_include: + # NOTE: Assumes relationship is to-many + for identifier in rel_obj["data"]: + if identifier not in unique_identifiers: + unique_identifiers.append(identifier) + # TODO: I think this could be improved + included = [] + for identifier in unique_identifiers: + res_type = identifier["type"] + if res_type == "track": + track_id = int(identifier["id"]) + track = current_app.config["lib"].get_item(track_id) + included.append(TrackDocument.resource_object(track)) + elif res_type == "album": + album_id = int(identifier["id"]) + album = current_app.config["lib"].get_album(album_id) + included.append(AlbumDocument.resource_object(album)) + elif res_type == "artist": + artist_id = identifier["id"] + included.append(ArtistDocument.resource_object(artist_id)) + elif res_type == "image": + image_id = identifier["id"] + included.append(ImageDocument.resource_object(image_id)) + else: + raise ValueError(f"Invalid resource type: {res_type}") + return included + + def all_resources(self): + """Build document for /tracks, /albums or /artists.""" + query = self.translate_filters() + sort_arg = request.args.get("sort", None) + if sort_arg: + sort = self.translate_sorts(sort_arg) + # For each sort field add a query which ensures all results + # have a non-empty, non-zero value for that field. + for s in sort.sorts: + query.subqueries.append( + NotQuery( + # Match empty fields (^$) or zero fields, (^0$) + RegexpQuery(s.field, "(^$|^0$)", fast=False) + ) + ) + else: + sort = None + # Get information from the library + collection = self.get_collection(query=query, sort=sort) + # Convert info to AURA form and paginate it + data, next_url = self.paginate(collection) + document = {"data": data} + # If there are more pages then provide a way to access them + if next_url: + document["links"] = {"next": next_url} + # Include related resources for each element in "data" + include_str = request.args.get("include", None) + if include_str: + document["included"] = self.get_included(data, include_str) + return document + + def single_resource_document(self, resource_object): + """Build document for a specific requested resource. + + Args: + resource_object: A dictionary in the form of a JSON:API + resource object. + """ + document = {"data": resource_object} + include_str = request.args.get("include", None) + if include_str: + # [document["data"]] is because arg needs to be list + document["included"] = self.get_included( + [document["data"]], include_str + ) + return document + + +class TrackDocument(AURADocument): + """Class for building documents for /tracks endpoints.""" + + attribute_map = TRACK_ATTR_MAP + + def get_collection(self, query=None, sort=None): + """Get Item objects from the library. + + Args: + query: A beets Query object or a beets query string. + sort: A beets Sort object. + """ + return current_app.config["lib"].items(query, sort) + + def get_attribute_converter(self, beets_attr): + """Work out what data type an attribute should be for beets. + + Args: + beets_attr: The name of the beets attribute, e.g. "title". + """ + # filesize is a special field (read from disk not db?) + if beets_attr == "filesize": + converter = int + else: + try: + # Look for field in list of Item fields + # and get python type of database type. + # See beets.library.Item and beets.dbcore.types + converter = Item._fields[beets_attr].model_type + except KeyError: + # Fall back to string (NOTE: probably not good) + converter = str + return converter + + @staticmethod + def resource_object(track): + """Construct a JSON:API resource object from a beets Item. + + Args: + track: A beets Item object. + """ + attributes = {} + # Use aura => beets attribute map, e.g. size => filesize + for aura_attr, beets_attr in TRACK_ATTR_MAP.items(): + a = getattr(track, beets_attr) + # Only set attribute if it's not None, 0, "", etc. + # NOTE: This could result in required attributes not being set + if a: + attributes[aura_attr] = a + + # JSON:API one-to-many relationship to parent album + relationships = { + "artists": {"data": [{"type": "artist", "id": track.artist}]} + } + # Only add album relationship if not singleton + if not track.singleton: + relationships["albums"] = { + "data": [{"type": "album", "id": str(track.album_id)}] + } + + return { + "type": "track", + "id": str(track.id), + "attributes": attributes, + "relationships": relationships, + } + + def single_resource(self, track_id): + """Get track from the library and build a document. + + Args: + track_id: The beets id of the track (integer). + """ + track = current_app.config["lib"].get_item(track_id) + if not track: + return self.error( + "404 Not Found", + "No track with the requested id.", + "There is no track with an id of {} in the library.".format( + track_id + ), + ) + return self.single_resource_document(self.resource_object(track)) + + +class AlbumDocument(AURADocument): + """Class for building documents for /albums endpoints.""" + + attribute_map = ALBUM_ATTR_MAP + + def get_collection(self, query=None, sort=None): + """Get Album objects from the library. + + Args: + query: A beets Query object or a beets query string. + sort: A beets Sort object. + """ + return current_app.config["lib"].albums(query, sort) + + def get_attribute_converter(self, beets_attr): + """Work out what data type an attribute should be for beets. + + Args: + beets_attr: The name of the beets attribute, e.g. "title". + """ + try: + # Look for field in list of Album fields + # and get python type of database type. + # See beets.library.Album and beets.dbcore.types + converter = Album._fields[beets_attr].model_type + except KeyError: + # Fall back to string (NOTE: probably not good) + converter = str + return converter + + @staticmethod + def resource_object(album): + """Construct a JSON:API resource object from a beets Album. + + Args: + album: A beets Album object. + """ + attributes = {} + # Use aura => beets attribute name map + for aura_attr, beets_attr in ALBUM_ATTR_MAP.items(): + a = getattr(album, beets_attr) + # Only set attribute if it's not None, 0, "", etc. + # NOTE: This could mean required attributes are not set + if a: + attributes[aura_attr] = a + + # Get beets Item objects for all tracks in the album sorted by + # track number. Sorting is not required but it's nice. + query = MatchQuery("album_id", album.id) + sort = FixedFieldSort("track", ascending=True) + tracks = current_app.config["lib"].items(query, sort) + # JSON:API one-to-many relationship to tracks on the album + relationships = { + "tracks": { + "data": [{"type": "track", "id": str(t.id)} for t in tracks] + } + } + # Add images relationship if album has associated images + if album.artpath: + path = py3_path(album.artpath) + filename = path.split("/")[-1] + image_id = f"album-{album.id}-{filename}" + relationships["images"] = { + "data": [{"type": "image", "id": image_id}] + } + # Add artist relationship if artist name is same on tracks + # Tracks are used to define artists so don't albumartist + # Check for all tracks in case some have featured artists + if album.albumartist in [t.artist for t in tracks]: + relationships["artists"] = { + "data": [{"type": "artist", "id": album.albumartist}] + } + + return { + "type": "album", + "id": str(album.id), + "attributes": attributes, + "relationships": relationships, + } + + def single_resource(self, album_id): + """Get album from the library and build a document. + + Args: + album_id: The beets id of the album (integer). + """ + album = current_app.config["lib"].get_album(album_id) + if not album: + return self.error( + "404 Not Found", + "No album with the requested id.", + "There is no album with an id of {} in the library.".format( + album_id + ), + ) + return self.single_resource_document(self.resource_object(album)) + + +class ArtistDocument(AURADocument): + """Class for building documents for /artists endpoints.""" + + attribute_map = ARTIST_ATTR_MAP + + def get_collection(self, query=None, sort=None): + """Get a list of artist names from the library. + + Args: + query: A beets Query object or a beets query string. + sort: A beets Sort object. + """ + # Gets only tracks with matching artist information + tracks = current_app.config["lib"].items(query, sort) + collection = [] + for track in tracks: + # Do not add duplicates + if track.artist not in collection: + collection.append(track.artist) + return collection + + def get_attribute_converter(self, beets_attr): + """Work out what data type an attribute should be for beets. + + Args: + beets_attr: The name of the beets attribute, e.g. "artist". + """ + try: + # Look for field in list of Item fields + # and get python type of database type. + # See beets.library.Item and beets.dbcore.types + converter = Item._fields[beets_attr].model_type + except KeyError: + # Fall back to string (NOTE: probably not good) + converter = str + return converter + + @staticmethod + def resource_object(artist_id): + """Construct a JSON:API resource object for the given artist. + + Args: + artist_id: A string which is the artist's name. + """ + # Get tracks where artist field exactly matches artist_id + query = MatchQuery("artist", artist_id) + tracks = current_app.config["lib"].items(query) + if not tracks: + return None + + # Get artist information from the first track + # NOTE: It could be that the first track doesn't have a + # MusicBrainz id but later tracks do, which isn't ideal. + attributes = {} + # Use aura => beets attribute map, e.g. artist => name + for aura_attr, beets_attr in ARTIST_ATTR_MAP.items(): + a = getattr(tracks[0], beets_attr) + # Only set attribute if it's not None, 0, "", etc. + # NOTE: This could mean required attributes are not set + if a: + attributes[aura_attr] = a + + relationships = { + "tracks": { + "data": [{"type": "track", "id": str(t.id)} for t in tracks] + } + } + album_query = MatchQuery("albumartist", artist_id) + albums = current_app.config["lib"].albums(query=album_query) + if len(albums) != 0: + relationships["albums"] = { + "data": [{"type": "album", "id": str(a.id)} for a in albums] + } + + return { + "type": "artist", + "id": artist_id, + "attributes": attributes, + "relationships": relationships, + } + + def single_resource(self, artist_id): + """Get info for the requested artist and build a document. + + Args: + artist_id: A string which is the artist's name. + """ + artist_resource = self.resource_object(artist_id) + if not artist_resource: + return self.error( + "404 Not Found", + "No artist with the requested id.", + "There is no artist with an id of {} in the library.".format( + artist_id + ), + ) + return self.single_resource_document(artist_resource) + + +def safe_filename(fn): + """Check whether a string is a simple (non-path) filename. + + For example, `foo.txt` is safe because it is a "plain" filename. But + `foo/bar.txt` and `../foo.txt` and `.` are all non-safe because they + can traverse to other directories other than the current one. + """ + # Rule out any directories. + if os.path.basename(fn) != fn: + return False + + # In single names, rule out Unix directory traversal names. + if fn in ('.', '..'): + return False + + return True + + +class ImageDocument(AURADocument): + """Class for building documents for /images/(id) endpoints.""" + + @staticmethod + def get_image_path(image_id): + """Works out the full path to the image with the given id. + + Returns None if there is no such image. + + Args: + image_id: A string in the form + "--". + """ + # Split image_id into its constituent parts + id_split = image_id.split("-") + if len(id_split) < 3: + # image_id is not in the required format + return None + parent_type = id_split[0] + parent_id = id_split[1] + img_filename = "-".join(id_split[2:]) + if not safe_filename(img_filename): + return None + + # Get the path to the directory parent's images are in + if parent_type == "album": + album = current_app.config["lib"].get_album(int(parent_id)) + if not album or not album.artpath: + return None + # Cut the filename off of artpath + # This is in preparation for supporting images in the same + # directory that are not tracked by beets. + artpath = py3_path(album.artpath) + dir_path = "/".join(artpath.split("/")[:-1]) + else: + # Images for other resource types are not supported + return None + + img_path = os.path.join(dir_path, img_filename) + # Check the image actually exists + if isfile(img_path): + return img_path + else: + return None + + @staticmethod + def resource_object(image_id): + """Construct a JSON:API resource object for the given image. + + Args: + image_id: A string in the form + "--". + """ + # Could be called as a static method, so can't use + # self.get_image_path() + image_path = ImageDocument.get_image_path(image_id) + if not image_path: + return None + + attributes = { + "role": "cover", + "mimetype": guess_type(image_path)[0], + "size": getsize(image_path), + } + try: + from PIL import Image + except ImportError: + pass + else: + im = Image.open(image_path) + attributes["width"] = im.width + attributes["height"] = im.height + + relationships = {} + # Split id into [parent_type, parent_id, filename] + id_split = image_id.split("-") + relationships[id_split[0] + "s"] = { + "data": [{"type": id_split[0], "id": id_split[1]}] + } + + return { + "id": image_id, + "type": "image", + # Remove attributes that are None, 0, "", etc. + "attributes": {k: v for k, v in attributes.items() if v}, + "relationships": relationships, + } + + def single_resource(self, image_id): + """Get info for the requested image and build a document. + + Args: + image_id: A string in the form + "--". + """ + image_resource = self.resource_object(image_id) + if not image_resource: + return self.error( + "404 Not Found", + "No image with the requested id.", + "There is no image with an id of {} in the library.".format( + image_id + ), + ) + return self.single_resource_document(image_resource) + + +# Initialise flask blueprint +aura_bp = Blueprint("aura_bp", __name__) + + +@aura_bp.route("/server") +def server_info(): + """Respond with info about the server.""" + return {"data": {"type": "server", "id": "0", "attributes": SERVER_INFO}} + + +# Track endpoints + + +@aura_bp.route("/tracks") +def all_tracks(): + """Respond with a list of all tracks and related information.""" + doc = TrackDocument() + return doc.all_resources() + + +@aura_bp.route("/tracks/") +def single_track(track_id): + """Respond with info about the specified track. + + Args: + track_id: The id of the track provided in the URL (integer). + """ + doc = TrackDocument() + return doc.single_resource(track_id) + + +@aura_bp.route("/tracks//audio") +def audio_file(track_id): + """Supply an audio file for the specified track. + + Args: + track_id: The id of the track provided in the URL (integer). + """ + track = current_app.config["lib"].get_item(track_id) + if not track: + return AURADocument.error( + "404 Not Found", + "No track with the requested id.", + "There is no track with an id of {} in the library.".format( + track_id + ), + ) + + path = py3_path(track.path) + if not isfile(path): + return AURADocument.error( + "404 Not Found", + "No audio file for the requested track.", + ( + "There is no audio file for track {} at the expected location" + ).format(track_id), + ) + + file_mimetype = guess_type(path)[0] + if not file_mimetype: + return AURADocument.error( + "500 Internal Server Error", + "Requested audio file has an unknown mimetype.", + ( + "The audio file for track {} has an unknown mimetype. " + "Its file extension is {}." + ).format(track_id, path.split(".")[-1]), + ) + + # Check that the Accept header contains the file's mimetype + # Takes into account */* and audio/* + # Adding support for the bitrate parameter would require some effort so I + # left it out. This means the client could be sent an error even if the + # audio doesn't need transcoding. + if not request.accept_mimetypes.best_match([file_mimetype]): + return AURADocument.error( + "406 Not Acceptable", + "Unsupported MIME type or bitrate parameter in Accept header.", + ( + "The audio file for track {} is only available as {} and " + "bitrate parameters are not supported." + ).format(track_id, file_mimetype), + ) + + return send_file( + path, + mimetype=file_mimetype, + # Handles filename in Content-Disposition header + as_attachment=True, + # Tries to upgrade the stream to support range requests + conditional=True, + ) + + +# Album endpoints + + +@aura_bp.route("/albums") +def all_albums(): + """Respond with a list of all albums and related information.""" + doc = AlbumDocument() + return doc.all_resources() + + +@aura_bp.route("/albums/") +def single_album(album_id): + """Respond with info about the specified album. + + Args: + album_id: The id of the album provided in the URL (integer). + """ + doc = AlbumDocument() + return doc.single_resource(album_id) + + +# Artist endpoints +# Artist ids are their names + + +@aura_bp.route("/artists") +def all_artists(): + """Respond with a list of all artists and related information.""" + doc = ArtistDocument() + return doc.all_resources() + + +# Using the path converter allows slashes in artist_id +@aura_bp.route("/artists/") +def single_artist(artist_id): + """Respond with info about the specified artist. + + Args: + artist_id: The id of the artist provided in the URL. A string + which is the artist's name. + """ + doc = ArtistDocument() + return doc.single_resource(artist_id) + + +# Image endpoints +# Image ids are in the form -- +# For example: album-13-cover.jpg + + +@aura_bp.route("/images/") +def single_image(image_id): + """Respond with info about the specified image. + + Args: + image_id: The id of the image provided in the URL. A string in + the form "--". + """ + doc = ImageDocument() + return doc.single_resource(image_id) + + +@aura_bp.route("/images//file") +def image_file(image_id): + """Supply an image file for the specified image. + + Args: + image_id: The id of the image provided in the URL. A string in + the form "--". + """ + img_path = ImageDocument.get_image_path(image_id) + if not img_path: + return AURADocument.error( + "404 Not Found", + "No image with the requested id.", + "There is no image with an id of {} in the library".format( + image_id + ), + ) + return send_file(img_path) + + +# WSGI app + + +def create_app(): + """An application factory for use by a WSGI server.""" + config["aura"].add( + { + "host": "127.0.0.1", + "port": 8337, + "cors": [], + "cors_supports_credentials": False, + "page_limit": 500, + } + ) + + app = Flask(__name__) + # Register AURA blueprint view functions under a URL prefix + app.register_blueprint(aura_bp, url_prefix="/aura") + # AURA specifies mimetype MUST be this + app.config["JSONIFY_MIMETYPE"] = "application/vnd.api+json" + # Disable auto-sorting of JSON keys + app.config["JSON_SORT_KEYS"] = False + # Provide a way to access the beets library + # The normal method of using the Library and config provided in the + # command function is not used because create_app() could be called + # by an external WSGI server. + # NOTE: this uses a 'private' function from beets.ui.__init__ + app.config["lib"] = _open_library(config) + + # Enable CORS if required + cors = config["aura"]["cors"].as_str_seq(list) + if cors: + from flask_cors import CORS + + # "Accept" is the only header clients use + app.config["CORS_ALLOW_HEADERS"] = "Accept" + app.config["CORS_RESOURCES"] = {r"/aura/*": {"origins": cors}} + app.config["CORS_SUPPORTS_CREDENTIALS"] = config["aura"][ + "cors_supports_credentials" + ].get(bool) + CORS(app) + + return app + + +# Beets Plugin Hook + + +class AURAPlugin(BeetsPlugin): + """The BeetsPlugin subclass for the AURA server plugin.""" + + def __init__(self): + """Add configuration options for the AURA plugin.""" + super().__init__() + + def commands(self): + """Add subcommand used to run the AURA server.""" + + def run_aura(lib, opts, args): + """Run the application using Flask's built in-server. + + Args: + lib: A beets Library object (not used). + opts: Command line options. An optparse.Values object. + args: The list of arguments to process (not used). + """ + app = create_app() + # Start the built-in server (not intended for production) + app.run( + host=self.config["host"].get(str), + port=self.config["port"].get(int), + debug=opts.debug, + threaded=True, + ) + + run_aura_cmd = Subcommand("aura", help="run an AURA server") + run_aura_cmd.parser.add_option( + "-d", + "--debug", + action="store_true", + default=False, + help="use Flask debug mode", + ) + run_aura_cmd.func = run_aura + return [run_aura_cmd] diff --git a/libs/common/beetsplug/badfiles.py b/libs/common/beetsplug/badfiles.py index 62c6d8af..ec465895 100644 --- a/libs/common/beetsplug/badfiles.py +++ b/libs/common/beetsplug/badfiles.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, François-Xavier Thomas. # @@ -16,18 +15,19 @@ """Use command-line tools to check for audio file corruption. """ -from __future__ import division, absolute_import, print_function -from beets.plugins import BeetsPlugin -from beets.ui import Subcommand -from beets.util import displayable_path, confit -from beets import ui from subprocess import check_output, CalledProcessError, list2cmdline, STDOUT + import shlex import os import errno import sys -import six +import confuse +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand +from beets.util import displayable_path, par_map +from beets import ui +from beets import importer class CheckerCommandException(Exception): @@ -48,8 +48,17 @@ class CheckerCommandException(Exception): class BadFiles(BeetsPlugin): + def __init__(self): + super().__init__() + self.verbose = False + + self.register_listener('import_task_start', + self.on_import_task_start) + self.register_listener('import_task_before_choice', + self.on_import_task_before_choice) + def run_command(self, cmd): - self._log.debug(u"running command: {}", + self._log.debug("running command: {}", displayable_path(list2cmdline(cmd))) try: output = check_output(cmd, stderr=STDOUT) @@ -61,7 +70,7 @@ class BadFiles(BeetsPlugin): status = e.returncode except OSError as e: raise CheckerCommandException(cmd, e) - output = output.decode(sys.getfilesystemencoding()) + output = output.decode(sys.getdefaultencoding(), 'replace') return status, errors, [line for line in output.split("\n") if line] def check_mp3val(self, path): @@ -85,68 +94,122 @@ class BadFiles(BeetsPlugin): ext = ext.lower() try: command = self.config['commands'].get(dict).get(ext) - except confit.NotFoundError: + except confuse.NotFoundError: command = None if command: return self.check_custom(command) - elif ext == "mp3": + if ext == "mp3": return self.check_mp3val - elif ext == "flac": + if ext == "flac": return self.check_flac - def check_bad(self, lib, opts, args): - for item in lib.items(ui.decargs(args)): + def check_item(self, item): + # First, check whether the path exists. If not, the user + # should probably run `beet update` to cleanup your library. + dpath = displayable_path(item.path) + self._log.debug("checking path: {}", dpath) + if not os.path.exists(item.path): + ui.print_("{}: file does not exist".format( + ui.colorize('text_error', dpath))) - # First, check whether the path exists. If not, the user - # should probably run `beet update` to cleanup your library. - dpath = displayable_path(item.path) - self._log.debug(u"checking path: {}", dpath) - if not os.path.exists(item.path): - ui.print_(u"{}: file does not exist".format( - ui.colorize('text_error', dpath))) + # Run the checker against the file if one is found + ext = os.path.splitext(item.path)[1][1:].decode('utf8', 'ignore') + checker = self.get_checker(ext) + if not checker: + self._log.error("no checker specified in the config for {}", + ext) + return [] + path = item.path + if not isinstance(path, str): + path = item.path.decode(sys.getfilesystemencoding()) + try: + status, errors, output = checker(path) + except CheckerCommandException as e: + if e.errno == errno.ENOENT: + self._log.error( + "command not found: {} when validating file: {}", + e.checker, + e.path + ) + else: + self._log.error("error invoking {}: {}", e.checker, e.msg) + return [] - # Run the checker against the file if one is found - ext = os.path.splitext(item.path)[1][1:].decode('utf8', 'ignore') - checker = self.get_checker(ext) - if not checker: - self._log.error(u"no checker specified in the config for {}", - ext) - continue - path = item.path - if not isinstance(path, six.text_type): - path = item.path.decode(sys.getfilesystemencoding()) - try: - status, errors, output = checker(path) - except CheckerCommandException as e: - if e.errno == errno.ENOENT: - self._log.error( - u"command not found: {} when validating file: {}", - e.checker, - e.path - ) - else: - self._log.error(u"error invoking {}: {}", e.checker, e.msg) - continue - if status > 0: - ui.print_(u"{}: checker exited with status {}" - .format(ui.colorize('text_error', dpath), status)) - for line in output: - ui.print_(u" {}".format(displayable_path(line))) - elif errors > 0: - ui.print_(u"{}: checker found {} errors or warnings" - .format(ui.colorize('text_warning', dpath), errors)) - for line in output: - ui.print_(u" {}".format(displayable_path(line))) - elif opts.verbose: - ui.print_(u"{}: ok".format(ui.colorize('text_success', dpath))) + error_lines = [] + + if status > 0: + error_lines.append( + "{}: checker exited with status {}" + .format(ui.colorize('text_error', dpath), status)) + for line in output: + error_lines.append(f" {line}") + + elif errors > 0: + error_lines.append( + "{}: checker found {} errors or warnings" + .format(ui.colorize('text_warning', dpath), errors)) + for line in output: + error_lines.append(f" {line}") + elif self.verbose: + error_lines.append( + "{}: ok".format(ui.colorize('text_success', dpath))) + + return error_lines + + def on_import_task_start(self, task, session): + if not self.config['check_on_import'].get(False): + return + + checks_failed = [] + + for item in task.items: + error_lines = self.check_item(item) + if error_lines: + checks_failed.append(error_lines) + + if checks_failed: + task._badfiles_checks_failed = checks_failed + + def on_import_task_before_choice(self, task, session): + if hasattr(task, '_badfiles_checks_failed'): + ui.print_('{} one or more files failed checks:' + .format(ui.colorize('text_warning', 'BAD'))) + for error in task._badfiles_checks_failed: + for error_line in error: + ui.print_(error_line) + + ui.print_() + ui.print_('What would you like to do?') + + sel = ui.input_options(['aBort', 'skip', 'continue']) + + if sel == 's': + return importer.action.SKIP + elif sel == 'c': + return None + elif sel == 'b': + raise importer.ImportAbort() + else: + raise Exception(f'Unexpected selection: {sel}') + + def command(self, lib, opts, args): + # Get items from arguments + items = lib.items(ui.decargs(args)) + self.verbose = opts.verbose + + def check_and_print(item): + for error_line in self.check_item(item): + ui.print_(error_line) + + par_map(check_and_print, items) def commands(self): bad_command = Subcommand('bad', - help=u'check for corrupt or missing files') + help='check for corrupt or missing files') bad_command.parser.add_option( - u'-v', u'--verbose', + '-v', '--verbose', action='store_true', default=False, dest='verbose', - help=u'view results for both the bad and uncorrupted files' + help='view results for both the bad and uncorrupted files' ) - bad_command.func = self.check_bad + bad_command.func = self.command return [bad_command] diff --git a/libs/common/beetsplug/bareasc.py b/libs/common/beetsplug/bareasc.py new file mode 100644 index 00000000..21836936 --- /dev/null +++ b/libs/common/beetsplug/bareasc.py @@ -0,0 +1,82 @@ +# This file is part of beets. +# Copyright 2016, Philippe Mongeau. +# Copyright 2021, Graham R. Cobb. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and ascociated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# This module is adapted from Fuzzy in accordance to the licence of +# that module + +"""Provides a bare-ASCII matching query.""" + + +from beets import ui +from beets.ui import print_, decargs +from beets.plugins import BeetsPlugin +from beets.dbcore.query import StringFieldQuery +from unidecode import unidecode + + +class BareascQuery(StringFieldQuery): + """Compare items using bare ASCII, without accents etc.""" + @classmethod + def string_match(cls, pattern, val): + """Convert both pattern and string to plain ASCII before matching. + + If pattern is all lower case, also convert string to lower case so + match is also case insensitive + """ + # smartcase + if pattern.islower(): + val = val.lower() + pattern = unidecode(pattern) + val = unidecode(val) + return pattern in val + + +class BareascPlugin(BeetsPlugin): + """Plugin to provide bare-ASCII option for beets matching.""" + def __init__(self): + """Default prefix for selecting bare-ASCII matching is #.""" + super().__init__() + self.config.add({ + 'prefix': '#', + }) + + def queries(self): + """Register bare-ASCII matching.""" + prefix = self.config['prefix'].as_str() + return {prefix: BareascQuery} + + def commands(self): + """Add bareasc command as unidecode version of 'list'.""" + cmd = ui.Subcommand('bareasc', + help='unidecode version of beet list command') + cmd.parser.usage += "\n" \ + 'Example: %prog -f \'$album: $title\' artist:beatles' + cmd.parser.add_all_common_options() + cmd.func = self.unidecode_list + return [cmd] + + def unidecode_list(self, lib, opts, args): + """Emulate normal 'list' command but with unidecode output.""" + query = decargs(args) + album = opts.album + # Copied from commands.py - list_items + if album: + for album in lib.albums(query): + bare = unidecode(str(album)) + print_(bare) + else: + for item in lib.items(query): + bare = unidecode(str(item)) + print_(bare) diff --git a/libs/common/beetsplug/beatport.py b/libs/common/beetsplug/beatport.py index fc412d99..133441d7 100644 --- a/libs/common/beetsplug/beatport.py +++ b/libs/common/beetsplug/beatport.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,11 +14,9 @@ """Adds Beatport release and track search support to the autotagger """ -from __future__ import division, absolute_import, print_function import json import re -import six from datetime import datetime, timedelta from requests_oauthlib import OAuth1Session @@ -28,35 +25,35 @@ from requests_oauthlib.oauth1_session import (TokenRequestDenied, TokenMissing, import beets import beets.ui -from beets.autotag.hooks import AlbumInfo, TrackInfo, Distance -from beets.plugins import BeetsPlugin -from beets.util import confit +from beets.autotag.hooks import AlbumInfo, TrackInfo +from beets.plugins import BeetsPlugin, MetadataSourcePlugin, get_distance +import confuse AUTH_ERRORS = (TokenRequestDenied, TokenMissing, VerifierMissing) -USER_AGENT = u'beets/{0} +http://beets.io/'.format(beets.__version__) +USER_AGENT = f'beets/{beets.__version__} +https://beets.io/' class BeatportAPIError(Exception): pass -class BeatportObject(object): +class BeatportObject: def __init__(self, data): self.beatport_id = data['id'] - self.name = six.text_type(data['name']) + self.name = str(data['name']) if 'releaseDate' in data: self.release_date = datetime.strptime(data['releaseDate'], '%Y-%m-%d') if 'artists' in data: - self.artists = [(x['id'], six.text_type(x['name'])) + self.artists = [(x['id'], str(x['name'])) for x in data['artists']] if 'genres' in data: - self.genres = [six.text_type(x['name']) + self.genres = [str(x['name']) for x in data['genres']] -class BeatportClient(object): +class BeatportClient: _api_base = 'https://oauth-api.beatport.com' def __init__(self, c_key, c_secret, auth_key=None, auth_secret=None): @@ -109,7 +106,7 @@ class BeatportClient(object): :rtype: (unicode, unicode) tuple """ self.api.parse_authorization_response( - "http://beets.io/auth?" + auth_data) + "https://beets.io/auth?" + auth_data) access_data = self.api.fetch_access_token( self._make_url('/identity/1/oauth/access-token')) return access_data['oauth_token'], access_data['oauth_token_secret'] @@ -131,7 +128,7 @@ class BeatportClient(object): """ response = self._get('catalog/3/search', query=query, perPage=5, - facets=['fieldType:{0}'.format(release_type)]) + facets=[f'fieldType:{release_type}']) for item in response: if release_type == 'release': if details: @@ -150,9 +147,11 @@ class BeatportClient(object): :rtype: :py:class:`BeatportRelease` """ response = self._get('/catalog/3/releases', id=beatport_id) - release = BeatportRelease(response[0]) - release.tracks = self.get_release_tracks(beatport_id) - return release + if response: + release = BeatportRelease(response[0]) + release.tracks = self.get_release_tracks(beatport_id) + return release + return None def get_release_tracks(self, beatport_id): """ Get all tracks for a given release. @@ -191,7 +190,7 @@ class BeatportClient(object): response = self.api.get(self._make_url(endpoint), params=kwargs) except Exception as e: raise BeatportAPIError("Error connecting to Beatport API: {}" - .format(e.message)) + .format(e)) if not response: raise BeatportAPIError( "Error {0.status_code} for '{0.request.path_url}" @@ -199,21 +198,20 @@ class BeatportClient(object): return response.json()['results'] -@six.python_2_unicode_compatible class BeatportRelease(BeatportObject): def __str__(self): if len(self.artists) < 4: artist_str = ", ".join(x[1] for x in self.artists) else: artist_str = "Various Artists" - return u"".format( + return "".format( artist_str, self.name, self.catalog_number, ) def __repr__(self): - return six.text_type(self).encode('utf-8') + return str(self).encode('utf-8') def __init__(self, data): BeatportObject.__init__(self, data) @@ -224,26 +222,26 @@ class BeatportRelease(BeatportObject): if 'category' in data: self.category = data['category'] if 'slug' in data: - self.url = "http://beatport.com/release/{0}/{1}".format( + self.url = "https://beatport.com/release/{}/{}".format( data['slug'], data['id']) + self.genre = data.get('genre') -@six.python_2_unicode_compatible class BeatportTrack(BeatportObject): def __str__(self): artist_str = ", ".join(x[1] for x in self.artists) - return (u"" + return ("" .format(artist_str, self.name, self.mix_name)) def __repr__(self): - return six.text_type(self).encode('utf-8') + return str(self).encode('utf-8') def __init__(self, data): BeatportObject.__init__(self, data) if 'title' in data: - self.title = six.text_type(data['title']) + self.title = str(data['title']) if 'mixName' in data: - self.mix_name = six.text_type(data['mixName']) + self.mix_name = str(data['mixName']) self.length = timedelta(milliseconds=data.get('lengthMs', 0) or 0) if not self.length: try: @@ -252,14 +250,26 @@ class BeatportTrack(BeatportObject): except ValueError: pass if 'slug' in data: - self.url = "http://beatport.com/track/{0}/{1}".format(data['slug'], - data['id']) + self.url = "https://beatport.com/track/{}/{}" \ + .format(data['slug'], data['id']) self.track_number = data.get('trackNumber') + self.bpm = data.get('bpm') + self.initial_key = str( + (data.get('key') or {}).get('shortName') + ) + + # Use 'subgenre' and if not present, 'genre' as a fallback. + if data.get('subGenres'): + self.genre = str(data['subGenres'][0].get('name')) + elif data.get('genres'): + self.genre = str(data['genres'][0].get('name')) class BeatportPlugin(BeetsPlugin): + data_source = 'Beatport' + def __init__(self): - super(BeatportPlugin, self).__init__() + super().__init__() self.config.add({ 'apikey': '57713c3906af6f5def151b33601389176b37b429', 'apisecret': 'b3fe08c93c80aefd749fe871a16cd2bb32e2b954', @@ -279,7 +289,7 @@ class BeatportPlugin(BeetsPlugin): try: with open(self._tokenfile()) as f: tokendata = json.load(f) - except IOError: + except OSError: # No token yet. Generate one. token, secret = self.authenticate(c_key, c_secret) else: @@ -294,22 +304,22 @@ class BeatportPlugin(BeetsPlugin): try: url = auth_client.get_authorize_url() except AUTH_ERRORS as e: - self._log.debug(u'authentication error: {0}', e) - raise beets.ui.UserError(u'communication with Beatport failed') + self._log.debug('authentication error: {0}', e) + raise beets.ui.UserError('communication with Beatport failed') - beets.ui.print_(u"To authenticate with Beatport, visit:") + beets.ui.print_("To authenticate with Beatport, visit:") beets.ui.print_(url) # Ask for the verifier data and validate it. - data = beets.ui.input_(u"Enter the string displayed in your browser:") + data = beets.ui.input_("Enter the string displayed in your browser:") try: token, secret = auth_client.get_access_token(data) except AUTH_ERRORS as e: - self._log.debug(u'authentication error: {0}', e) - raise beets.ui.UserError(u'Beatport token request failed') + self._log.debug('authentication error: {0}', e) + raise beets.ui.UserError('Beatport token request failed') # Save the token for later use. - self._log.debug(u'Beatport token {0}, secret {1}', token, secret) + self._log.debug('Beatport token {0}, secret {1}', token, secret) with open(self._tokenfile(), 'w') as f: json.dump({'token': token, 'secret': secret}, f) @@ -318,74 +328,80 @@ class BeatportPlugin(BeetsPlugin): def _tokenfile(self): """Get the path to the JSON file for storing the OAuth token. """ - return self.config['tokenfile'].get(confit.Filename(in_app_dir=True)) + return self.config['tokenfile'].get(confuse.Filename(in_app_dir=True)) def album_distance(self, items, album_info, mapping): - """Returns the beatport source weight and the maximum source weight + """Returns the Beatport source weight and the maximum source weight for albums. """ - dist = Distance() - if album_info.data_source == 'Beatport': - dist.add('source', self.config['source_weight'].as_number()) - return dist + return get_distance( + data_source=self.data_source, + info=album_info, + config=self.config + ) def track_distance(self, item, track_info): - """Returns the beatport source weight and the maximum source weight + """Returns the Beatport source weight and the maximum source weight for individual tracks. """ - dist = Distance() - if track_info.data_source == 'Beatport': - dist.add('source', self.config['source_weight'].as_number()) - return dist + return get_distance( + data_source=self.data_source, + info=track_info, + config=self.config + ) - def candidates(self, items, artist, release, va_likely): + def candidates(self, items, artist, release, va_likely, extra_tags=None): """Returns a list of AlbumInfo objects for beatport search results matching release and artist (if not various). """ if va_likely: query = release else: - query = '%s %s' % (artist, release) + query = f'{artist} {release}' try: return self._get_releases(query) except BeatportAPIError as e: - self._log.debug(u'API Error: {0} (query: {1})', e, query) + self._log.debug('API Error: {0} (query: {1})', e, query) return [] def item_candidates(self, item, artist, title): """Returns a list of TrackInfo objects for beatport search results matching title and artist. """ - query = '%s %s' % (artist, title) + query = f'{artist} {title}' try: return self._get_tracks(query) except BeatportAPIError as e: - self._log.debug(u'API Error: {0} (query: {1})', e, query) + self._log.debug('API Error: {0} (query: {1})', e, query) return [] def album_for_id(self, release_id): """Fetches a release by its Beatport ID and returns an AlbumInfo object - or None if the release is not found. + or None if the query is not a valid ID or release is not found. """ - self._log.debug(u'Searching for release {0}', release_id) + self._log.debug('Searching for release {0}', release_id) match = re.search(r'(^|beatport\.com/release/.+/)(\d+)$', release_id) if not match: + self._log.debug('Not a valid Beatport release ID.') return None release = self.client.get_release(match.group(2)) - album = self._get_album_info(release) - return album + if release: + return self._get_album_info(release) + return None def track_for_id(self, track_id): """Fetches a track by its Beatport ID and returns a TrackInfo object - or None if the track is not found. + or None if the track is not a valid Beatport ID or track is not found. """ - self._log.debug(u'Searching for track {0}', track_id) + self._log.debug('Searching for track {0}', track_id) match = re.search(r'(^|beatport\.com/track/.+/)(\d+)$', track_id) if not match: + self._log.debug('Not a valid Beatport track ID.') return None bp_track = self.client.get_track(match.group(2)) - track = self._get_track_info(bp_track) - return track + if bp_track is not None: + return self._get_track_info(bp_track) + return None def _get_releases(self, query): """Returns a list of AlbumInfo objects for a beatport search query. @@ -408,7 +424,7 @@ class BeatportPlugin(BeetsPlugin): va = len(release.artists) > 3 artist, artist_id = self._get_artist(release.artists) if va: - artist = u"Various Artists" + artist = "Various Artists" tracks = [self._get_track_info(x) for x in release.tracks] return AlbumInfo(album=release.name, album_id=release.beatport_id, @@ -418,40 +434,33 @@ class BeatportPlugin(BeetsPlugin): month=release.release_date.month, day=release.release_date.day, label=release.label_name, - catalognum=release.catalog_number, media=u'Digital', - data_source=u'Beatport', data_url=release.url) + catalognum=release.catalog_number, media='Digital', + data_source=self.data_source, data_url=release.url, + genre=release.genre) def _get_track_info(self, track): """Returns a TrackInfo object for a Beatport Track object. """ title = track.name - if track.mix_name != u"Original Mix": - title += u" ({0})".format(track.mix_name) + if track.mix_name != "Original Mix": + title += f" ({track.mix_name})" artist, artist_id = self._get_artist(track.artists) length = track.length.total_seconds() return TrackInfo(title=title, track_id=track.beatport_id, artist=artist, artist_id=artist_id, length=length, index=track.track_number, medium_index=track.track_number, - data_source=u'Beatport', data_url=track.url) + data_source=self.data_source, data_url=track.url, + bpm=track.bpm, initial_key=track.initial_key, + genre=track.genre) def _get_artist(self, artists): """Returns an artist string (all artists) and an artist_id (the main artist) for a list of Beatport release or track artists. """ - artist_id = None - bits = [] - for artist in artists: - if not artist_id: - artist_id = artist[0] - name = artist[1] - # Strip disambiguation number. - name = re.sub(r' \(\d+\)$', '', name) - # Move articles to the front. - name = re.sub(r'^(.*?), (a|an|the)$', r'\2 \1', name, flags=re.I) - bits.append(name) - artist = ', '.join(bits).replace(' ,', ',') or None - return artist, artist_id + return MetadataSourcePlugin.get_artist( + artists=artists, id_key=0, name_key=1 + ) def _get_tracks(self, query): """Returns a list of TrackInfo objects for a Beatport query. diff --git a/libs/common/beetsplug/bench.py b/libs/common/beetsplug/bench.py index 41f575cd..6dffbdda 100644 --- a/libs/common/beetsplug/bench.py +++ b/libs/common/beetsplug/bench.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,7 +15,6 @@ """Some simple performance benchmarks for beets. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui diff --git a/libs/common/beetsplug/bpd/__init__.py b/libs/common/beetsplug/bpd/__init__.py index 1049f0c7..07198b1b 100644 --- a/libs/common/beetsplug/bpd/__init__.py +++ b/libs/common/beetsplug/bpd/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -18,37 +17,38 @@ Beets library. Attempts to implement a compatible protocol to allow use of the wide range of MPD clients. """ -from __future__ import division, absolute_import, print_function import re +import sys from string import Template import traceback import random import time +import math +import inspect +import socket import beets from beets.plugins import BeetsPlugin import beets.ui -from beets import logging from beets import vfs from beets.util import bluelet from beets.library import Item from beets import dbcore -from beets.mediafile import MediaFile -import six +from mediafile import MediaFile -PROTOCOL_VERSION = '0.13.0' +PROTOCOL_VERSION = '0.16.0' BUFSIZE = 1024 -HELLO = u'OK MPD %s' % PROTOCOL_VERSION -CLIST_BEGIN = u'command_list_begin' -CLIST_VERBOSE_BEGIN = u'command_list_ok_begin' -CLIST_END = u'command_list_end' -RESP_OK = u'OK' -RESP_CLIST_VERBOSE = u'list_OK' -RESP_ERR = u'ACK' +HELLO = 'OK MPD %s' % PROTOCOL_VERSION +CLIST_BEGIN = 'command_list_begin' +CLIST_VERBOSE_BEGIN = 'command_list_ok_begin' +CLIST_END = 'command_list_end' +RESP_OK = 'OK' +RESP_CLIST_VERBOSE = 'list_OK' +RESP_ERR = 'ACK' -NEWLINE = u"\n" +NEWLINE = "\n" ERROR_NOT_LIST = 1 ERROR_ARG = 2 @@ -68,14 +68,18 @@ VOLUME_MAX = 100 SAFE_COMMANDS = ( # Commands that are available when unauthenticated. - u'close', u'commands', u'notcommands', u'password', u'ping', + 'close', 'commands', 'notcommands', 'password', 'ping', ) -ITEM_KEYS_WRITABLE = set(MediaFile.fields()).intersection(Item._fields.keys()) +# List of subsystems/events used by the `idle` command. +SUBSYSTEMS = [ + 'update', 'player', 'mixer', 'options', 'playlist', 'database', + # Related to unsupported commands: + 'stored_playlist', 'output', 'subscription', 'sticker', 'message', + 'partition', +] -# Loggers. -log = logging.getLogger('beets.bpd') -global_log = logging.getLogger('beets') +ITEM_KEYS_WRITABLE = set(MediaFile.fields()).intersection(Item._fields.keys()) # Gstreamer import error. @@ -95,7 +99,7 @@ class BPDError(Exception): self.cmd_name = cmd_name self.index = index - template = Template(u'$resp [$code@$index] {$cmd_name} $message') + template = Template('$resp [$code@$index] {$cmd_name} $message') def response(self): """Returns a string to be used as the response code for the @@ -124,9 +128,9 @@ def make_bpd_error(s_code, s_message): pass return NewBPDError -ArgumentTypeError = make_bpd_error(ERROR_ARG, u'invalid type for argument') -ArgumentIndexError = make_bpd_error(ERROR_ARG, u'argument out of range') -ArgumentNotFoundError = make_bpd_error(ERROR_NO_EXIST, u'argument not found') +ArgumentTypeError = make_bpd_error(ERROR_ARG, 'invalid type for argument') +ArgumentIndexError = make_bpd_error(ERROR_ARG, 'argument out of range') +ArgumentNotFoundError = make_bpd_error(ERROR_NO_EXIST, 'argument not found') def cast_arg(t, val): @@ -150,10 +154,20 @@ class BPDClose(Exception): should be closed. """ + +class BPDIdle(Exception): + """Raised by a command to indicate the client wants to enter the idle state + and should be notified when a relevant event happens. + """ + def __init__(self, subsystems): + super().__init__() + self.subsystems = set(subsystems) + + # Generic server infrastructure, implementing the basic protocol. -class BaseServer(object): +class BaseServer: """A MPD-compatible music player server. The functions with the `cmd_` prefix are invoked in response to @@ -166,34 +180,87 @@ class BaseServer(object): This is a generic superclass and doesn't support many commands. """ - def __init__(self, host, port, password): + def __init__(self, host, port, password, ctrl_port, log, ctrl_host=None): """Create a new server bound to address `host` and listening on port `port`. If `password` is given, it is required to do anything significant on the server. + A separate control socket is established listening to `ctrl_host` on + port `ctrl_port` which is used to forward notifications from the player + and can be sent debug commands (e.g. using netcat). """ self.host, self.port, self.password = host, port, password + self.ctrl_host, self.ctrl_port = ctrl_host or host, ctrl_port + self.ctrl_sock = None + self._log = log # Default server values. self.random = False self.repeat = False + self.consume = False + self.single = False self.volume = VOLUME_MAX self.crossfade = 0 + self.mixrampdb = 0.0 + self.mixrampdelay = float('nan') + self.replay_gain_mode = 'off' self.playlist = [] self.playlist_version = 0 self.current_index = -1 self.paused = False self.error = None + # Current connections + self.connections = set() + # Object for random numbers generation self.random_obj = random.Random() + def connect(self, conn): + """A new client has connected. + """ + self.connections.add(conn) + + def disconnect(self, conn): + """Client has disconnected; clean up residual state. + """ + self.connections.remove(conn) + def run(self): """Block and start listening for connections from clients. An interrupt (^C) closes the server. """ self.startup_time = time.time() - bluelet.run(bluelet.server(self.host, self.port, - Connection.handler(self))) + + def start(): + yield bluelet.spawn( + bluelet.server(self.ctrl_host, self.ctrl_port, + ControlConnection.handler(self))) + yield bluelet.server(self.host, self.port, + MPDConnection.handler(self)) + bluelet.run(start()) + + def dispatch_events(self): + """If any clients have idle events ready, send them. + """ + # We need a copy of `self.connections` here since clients might + # disconnect once we try and send to them, changing `self.connections`. + for conn in list(self.connections): + yield bluelet.spawn(conn.send_notifications()) + + def _ctrl_send(self, message): + """Send some data over the control socket. + If it's our first time, open the socket. The message should be a + string without a terminal newline. + """ + if not self.ctrl_sock: + self.ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.ctrl_sock.connect((self.ctrl_host, self.ctrl_port)) + self.ctrl_sock.sendall((message + '\n').encode('utf-8')) + + def _send_event(self, event): + """Notify subscribed connections of an event.""" + for conn in self.connections: + conn.notify(event) def _item_info(self, item): """An abstract method that should response lines containing a @@ -231,10 +298,10 @@ class BaseServer(object): def _succ_idx(self): """Returns the index for the next song to play. - It also considers random and repeat flags. + It also considers random, single and repeat flags. No boundaries are checked. """ - if self.repeat: + if self.repeat and self.single: return self.current_index if self.random: return self._random_idx() @@ -245,7 +312,7 @@ class BaseServer(object): It also considers random and repeat flags. No boundaries are checked. """ - if self.repeat: + if self.repeat and self.single: return self.current_index if self.random: return self._random_idx() @@ -255,9 +322,17 @@ class BaseServer(object): """Succeeds.""" pass + def cmd_idle(self, conn, *subsystems): + subsystems = subsystems or SUBSYSTEMS + for system in subsystems: + if system not in SUBSYSTEMS: + raise BPDError(ERROR_ARG, + f'Unrecognised idle event: {system}') + raise BPDIdle(subsystems) # put the connection into idle mode + def cmd_kill(self, conn): """Exits the server process.""" - exit(0) + sys.exit(0) def cmd_close(self, conn): """Closes the connection.""" @@ -269,20 +344,20 @@ class BaseServer(object): conn.authenticated = True else: conn.authenticated = False - raise BPDError(ERROR_PASSWORD, u'incorrect password') + raise BPDError(ERROR_PASSWORD, 'incorrect password') def cmd_commands(self, conn): """Lists the commands available to the user.""" if self.password and not conn.authenticated: # Not authenticated. Show limited list of commands. for cmd in SAFE_COMMANDS: - yield u'command: ' + cmd + yield 'command: ' + cmd else: # Authenticated. Show all commands. for func in dir(self): if func.startswith('cmd_'): - yield u'command: ' + func[4:] + yield 'command: ' + func[4:] def cmd_notcommands(self, conn): """Lists all unavailable commands.""" @@ -292,7 +367,7 @@ class BaseServer(object): if func.startswith('cmd_'): cmd = func[4:] if cmd not in SAFE_COMMANDS: - yield u'command: ' + cmd + yield 'command: ' + cmd else: # Authenticated. No commands are unavailable. @@ -306,29 +381,43 @@ class BaseServer(object): playlist, playlistlength, and xfade. """ yield ( - u'volume: ' + six.text_type(self.volume), - u'repeat: ' + six.text_type(int(self.repeat)), - u'random: ' + six.text_type(int(self.random)), - u'playlist: ' + six.text_type(self.playlist_version), - u'playlistlength: ' + six.text_type(len(self.playlist)), - u'xfade: ' + six.text_type(self.crossfade), + 'repeat: ' + str(int(self.repeat)), + 'random: ' + str(int(self.random)), + 'consume: ' + str(int(self.consume)), + 'single: ' + str(int(self.single)), + 'playlist: ' + str(self.playlist_version), + 'playlistlength: ' + str(len(self.playlist)), + 'mixrampdb: ' + str(self.mixrampdb), ) + if self.volume > 0: + yield 'volume: ' + str(self.volume) + + if not math.isnan(self.mixrampdelay): + yield 'mixrampdelay: ' + str(self.mixrampdelay) + if self.crossfade > 0: + yield 'xfade: ' + str(self.crossfade) + if self.current_index == -1: - state = u'stop' + state = 'stop' elif self.paused: - state = u'pause' + state = 'pause' else: - state = u'play' - yield u'state: ' + state + state = 'play' + yield 'state: ' + state if self.current_index != -1: # i.e., paused or playing current_id = self._item_id(self.playlist[self.current_index]) - yield u'song: ' + six.text_type(self.current_index) - yield u'songid: ' + six.text_type(current_id) + yield 'song: ' + str(self.current_index) + yield 'songid: ' + str(current_id) + if len(self.playlist) > self.current_index + 1: + # If there's a next song, report its index too. + next_id = self._item_id(self.playlist[self.current_index + 1]) + yield 'nextsong: ' + str(self.current_index + 1) + yield 'nextsongid: ' + str(next_id) if self.error: - yield u'error: ' + self.error + yield 'error: ' + self.error def cmd_clearerror(self, conn): """Removes the persistent error state of the server. This @@ -340,29 +429,82 @@ class BaseServer(object): def cmd_random(self, conn, state): """Set or unset random (shuffle) mode.""" self.random = cast_arg('intbool', state) + self._send_event('options') def cmd_repeat(self, conn, state): """Set or unset repeat mode.""" self.repeat = cast_arg('intbool', state) + self._send_event('options') + + def cmd_consume(self, conn, state): + """Set or unset consume mode.""" + self.consume = cast_arg('intbool', state) + self._send_event('options') + + def cmd_single(self, conn, state): + """Set or unset single mode.""" + # TODO support oneshot in addition to 0 and 1 [MPD 0.20] + self.single = cast_arg('intbool', state) + self._send_event('options') def cmd_setvol(self, conn, vol): """Set the player's volume level (0-100).""" vol = cast_arg(int, vol) if vol < VOLUME_MIN or vol > VOLUME_MAX: - raise BPDError(ERROR_ARG, u'volume out of range') + raise BPDError(ERROR_ARG, 'volume out of range') self.volume = vol + self._send_event('mixer') + + def cmd_volume(self, conn, vol_delta): + """Deprecated command to change the volume by a relative amount.""" + vol_delta = cast_arg(int, vol_delta) + return self.cmd_setvol(conn, self.volume + vol_delta) def cmd_crossfade(self, conn, crossfade): """Set the number of seconds of crossfading.""" crossfade = cast_arg(int, crossfade) if crossfade < 0: - raise BPDError(ERROR_ARG, u'crossfade time must be nonnegative') + raise BPDError(ERROR_ARG, 'crossfade time must be nonnegative') + self._log.warning('crossfade is not implemented in bpd') + self.crossfade = crossfade + self._send_event('options') + + def cmd_mixrampdb(self, conn, db): + """Set the mixramp normalised max volume in dB.""" + db = cast_arg(float, db) + if db > 0: + raise BPDError(ERROR_ARG, 'mixrampdb time must be negative') + self._log.warning('mixramp is not implemented in bpd') + self.mixrampdb = db + self._send_event('options') + + def cmd_mixrampdelay(self, conn, delay): + """Set the mixramp delay in seconds.""" + delay = cast_arg(float, delay) + if delay < 0: + raise BPDError(ERROR_ARG, 'mixrampdelay time must be nonnegative') + self._log.warning('mixramp is not implemented in bpd') + self.mixrampdelay = delay + self._send_event('options') + + def cmd_replay_gain_mode(self, conn, mode): + """Set the replay gain mode.""" + if mode not in ['off', 'track', 'album', 'auto']: + raise BPDError(ERROR_ARG, 'Unrecognised replay gain mode') + self._log.warning('replay gain is not implemented in bpd') + self.replay_gain_mode = mode + self._send_event('options') + + def cmd_replay_gain_status(self, conn): + """Get the replaygain mode.""" + yield 'replay_gain_mode: ' + str(self.replay_gain_mode) def cmd_clear(self, conn): """Clear the playlist.""" self.playlist = [] self.playlist_version += 1 self.cmd_stop(conn) + self._send_event('playlist') def cmd_delete(self, conn, index): """Remove the song at index from the playlist.""" @@ -378,6 +520,7 @@ class BaseServer(object): elif index < self.current_index: # Deleted before playing. # Shift playing index down. self.current_index -= 1 + self._send_event('playlist') def cmd_deleteid(self, conn, track_id): self.cmd_delete(conn, self._id_to_index(track_id)) @@ -401,6 +544,7 @@ class BaseServer(object): self.current_index += 1 self.playlist_version += 1 + self._send_event('playlist') def cmd_moveid(self, conn, idx_from, idx_to): idx_from = self._id_to_index(idx_from) @@ -426,6 +570,7 @@ class BaseServer(object): self.current_index = i self.playlist_version += 1 + self._send_event('playlist') def cmd_swapid(self, conn, i_id, j_id): i = self._id_to_index(i_id) @@ -436,23 +581,27 @@ class BaseServer(object): """Indicates supported URL schemes. None by default.""" pass - def cmd_playlistinfo(self, conn, index=-1): + def cmd_playlistinfo(self, conn, index=None): """Gives metadata information about the entire playlist or a single track, given by its index. """ - index = cast_arg(int, index) - if index == -1: + if index is None: for track in self.playlist: yield self._item_info(track) else: + indices = self._parse_range(index, accept_single_number=True) try: - track = self.playlist[index] + tracks = [self.playlist[i] for i in indices] except IndexError: raise ArgumentIndexError() - yield self._item_info(track) + for track in tracks: + yield self._item_info(track) - def cmd_playlistid(self, conn, track_id=-1): - return self.cmd_playlistinfo(conn, self._id_to_index(track_id)) + def cmd_playlistid(self, conn, track_id=None): + if track_id is not None: + track_id = cast_arg(int, track_id) + track_id = self._id_to_index(track_id) + return self.cmd_playlistinfo(conn, track_id) def cmd_plchanges(self, conn, version): """Sends playlist changes since the given version. @@ -469,8 +618,8 @@ class BaseServer(object): Also a dummy implementation. """ for idx, track in enumerate(self.playlist): - yield u'cpos: ' + six.text_type(idx) - yield u'Id: ' + six.text_type(track.id) + yield 'cpos: ' + str(idx) + yield 'Id: ' + str(track.id) def cmd_currentsong(self, conn): """Sends information about the currently-playing song. @@ -481,20 +630,38 @@ class BaseServer(object): def cmd_next(self, conn): """Advance to the next song in the playlist.""" + old_index = self.current_index self.current_index = self._succ_idx() + if self.consume: + # TODO how does consume interact with single+repeat? + self.playlist.pop(old_index) + if self.current_index > old_index: + self.current_index -= 1 + self.playlist_version += 1 + self._send_event("playlist") if self.current_index >= len(self.playlist): - # Fallen off the end. Just move to stopped state. + # Fallen off the end. Move to stopped state or loop. + if self.repeat: + self.current_index = -1 + return self.cmd_play(conn) + return self.cmd_stop(conn) + elif self.single and not self.repeat: return self.cmd_stop(conn) else: return self.cmd_play(conn) def cmd_previous(self, conn): """Step back to the last song.""" + old_index = self.current_index self.current_index = self._prev_idx() + if self.consume: + self.playlist.pop(old_index) if self.current_index < 0: - return self.cmd_stop(conn) - else: - return self.cmd_play(conn) + if self.repeat: + self.current_index = len(self.playlist) - 1 + else: + self.current_index = 0 + return self.cmd_play(conn) def cmd_pause(self, conn, state=None): """Set the pause state playback.""" @@ -502,12 +669,13 @@ class BaseServer(object): self.paused = not self.paused # Toggle. else: self.paused = cast_arg('intbool', state) + self._send_event('player') def cmd_play(self, conn, index=-1): """Begin playback, possibly at a specified playlist index.""" index = cast_arg(int, index) - if index < -1 or index > len(self.playlist): + if index < -1 or index >= len(self.playlist): raise ArgumentIndexError() if index == -1: # No index specified: start where we are. @@ -521,6 +689,7 @@ class BaseServer(object): self.current_index = index self.paused = False + self._send_event('player') def cmd_playid(self, conn, track_id=0): track_id = cast_arg(int, track_id) @@ -534,6 +703,7 @@ class BaseServer(object): """Stop playback.""" self.current_index = -1 self.paused = False + self._send_event('player') def cmd_seek(self, conn, index, pos): """Seek to a specified point in a specified song.""" @@ -541,28 +711,40 @@ class BaseServer(object): if index < 0 or index >= len(self.playlist): raise ArgumentIndexError() self.current_index = index + self._send_event('player') def cmd_seekid(self, conn, track_id, pos): index = self._id_to_index(track_id) return self.cmd_seek(conn, index, pos) - def cmd_profile(self, conn): - """Memory profiling for debugging.""" - from guppy import hpy - heap = hpy().heap() - print(heap) + # Additions to the MPD protocol. + + def cmd_crash_TypeError(self, conn): # noqa: N802 + """Deliberately trigger a TypeError for testing purposes. + We want to test that the server properly responds with ERROR_SYSTEM + without crashing, and that this is not treated as ERROR_ARG (since it + is caused by a programming error, not a protocol error). + """ + 'a' + 2 -class Connection(object): - """A connection between a client and the server. Handles input and - output from and to the client. +class Connection: + """A connection between a client and the server. """ def __init__(self, server, sock): """Create a new connection for the accepted socket `client`. """ self.server = server self.sock = sock - self.authenticated = False + self.address = '{}:{}'.format(*sock.sock.getpeername()) + + def debug(self, message, kind=' '): + """Log a debug message about this connection. + """ + self.server._log.debug('{}[{}]: {}', kind, self.address, message) + + def run(self): + pass def send(self, lines): """Send lines, which which is either a single string or an @@ -570,14 +752,35 @@ class Connection(object): added after every string. Returns a Bluelet event that sends the data. """ - if isinstance(lines, six.string_types): + if isinstance(lines, str): lines = [lines] out = NEWLINE.join(lines) + NEWLINE - log.debug('{}', out[:-1]) # Don't log trailing newline. - if isinstance(out, six.text_type): + for l in out.split(NEWLINE)[:-1]: + self.debug(l, kind='>') + if isinstance(out, str): out = out.encode('utf-8') return self.sock.sendall(out) + @classmethod + def handler(cls, server): + def _handle(sock): + """Creates a new `Connection` and runs it. + """ + return cls(server, sock).run() + return _handle + + +class MPDConnection(Connection): + """A connection that receives commands from an MPD-compatible client. + """ + def __init__(self, server, sock): + """Create a new connection for the accepted socket `client`. + """ + super().__init__(server, sock) + self.authenticated = False + self.notifications = set() + self.idle_subscriptions = set() + def do_command(self, command): """A coroutine that runs the given command and sends an appropriate response.""" @@ -590,28 +793,75 @@ class Connection(object): # Send success code. yield self.send(RESP_OK) + def disconnect(self): + """The connection has closed for any reason. + """ + self.server.disconnect(self) + self.debug('disconnected', kind='*') + + def notify(self, event): + """Queue up an event for sending to this client. + """ + self.notifications.add(event) + + def send_notifications(self, force_close_idle=False): + """Send the client any queued events now. + """ + pending = self.notifications.intersection(self.idle_subscriptions) + try: + for event in pending: + yield self.send(f'changed: {event}') + if pending or force_close_idle: + self.idle_subscriptions = set() + self.notifications = self.notifications.difference(pending) + yield self.send(RESP_OK) + except bluelet.SocketClosedError: + self.disconnect() # Client disappeared. + def run(self): """Send a greeting to the client and begin processing commands as they arrive. """ + self.debug('connected', kind='*') + self.server.connect(self) yield self.send(HELLO) clist = None # Initially, no command list is being constructed. while True: line = yield self.sock.readline() if not line: + self.disconnect() # Client disappeared. break line = line.strip() if not line: + err = BPDError(ERROR_UNKNOWN, 'No command given') + yield self.send(err.response()) + self.disconnect() # Client sent a blank line. break line = line.decode('utf8') # MPD protocol uses UTF-8. - log.debug(u'{}', line) + for l in line.split(NEWLINE): + self.debug(l, kind='<') + + if self.idle_subscriptions: + # The connection is in idle mode. + if line == 'noidle': + yield bluelet.call(self.send_notifications(True)) + else: + err = BPDError(ERROR_UNKNOWN, + f'Got command while idle: {line}') + yield self.send(err.response()) + break + continue + if line == 'noidle': + # When not in idle, this command sends no response. + continue if clist is not None: # Command list already opened. if line == CLIST_END: yield bluelet.call(self.do_command(clist)) clist = None # Clear the command list. + yield bluelet.call(self.server.dispatch_events()) else: clist.append(Command(line)) @@ -626,18 +876,74 @@ class Connection(object): except BPDClose: # Command indicates that the conn should close. self.sock.close() + self.disconnect() # Client explicitly closed. return - - @classmethod - def handler(cls, server): - def _handle(sock): - """Creates a new `Connection` and runs it. - """ - return cls(server, sock).run() - return _handle + except BPDIdle as e: + self.idle_subscriptions = e.subsystems + self.debug('awaiting: {}'.format(' '.join(e.subsystems)), + kind='z') + yield bluelet.call(self.server.dispatch_events()) -class Command(object): +class ControlConnection(Connection): + """A connection used to control BPD for debugging and internal events. + """ + def __init__(self, server, sock): + """Create a new connection for the accepted socket `client`. + """ + super().__init__(server, sock) + + def debug(self, message, kind=' '): + self.server._log.debug('CTRL {}[{}]: {}', kind, self.address, message) + + def run(self): + """Listen for control commands and delegate to `ctrl_*` methods. + """ + self.debug('connected', kind='*') + while True: + line = yield self.sock.readline() + if not line: + break # Client disappeared. + line = line.strip() + if not line: + break # Client sent a blank line. + line = line.decode('utf8') # Protocol uses UTF-8. + for l in line.split(NEWLINE): + self.debug(l, kind='<') + command = Command(line) + try: + func = command.delegate('ctrl_', self) + yield bluelet.call(func(*command.args)) + except (AttributeError, TypeError) as e: + yield self.send('ERROR: {}'.format(e.args[0])) + except Exception: + yield self.send(['ERROR: server error', + traceback.format_exc().rstrip()]) + + def ctrl_play_finished(self): + """Callback from the player signalling a song finished playing. + """ + yield bluelet.call(self.server.dispatch_events()) + + def ctrl_profile(self): + """Memory profiling for debugging. + """ + from guppy import hpy + heap = hpy().heap() + yield self.send(heap) + + def ctrl_nickname(self, oldlabel, newlabel): + """Rename a client in the log messages. + """ + for c in self.server.connections: + if c.address == oldlabel: + c.address = newlabel + break + else: + yield self.send(f'ERROR: no such client: {oldlabel}') + + +class Command: """A command issued by the client for processing by the server. """ @@ -657,27 +963,59 @@ class Command(object): if match[0]: # Quoted argument. arg = match[0] - arg = arg.replace(u'\\"', u'"').replace(u'\\\\', u'\\') + arg = arg.replace('\\"', '"').replace('\\\\', '\\') else: # Unquoted argument. arg = match[1] self.args.append(arg) + def delegate(self, prefix, target, extra_args=0): + """Get the target method that corresponds to this command. + The `prefix` is prepended to the command name and then the resulting + name is used to search `target` for a method with a compatible number + of arguments. + """ + # Attempt to get correct command function. + func_name = prefix + self.name + if not hasattr(target, func_name): + raise AttributeError(f'unknown command "{self.name}"') + func = getattr(target, func_name) + + argspec = inspect.getfullargspec(func) + + # Check that `func` is able to handle the number of arguments sent + # by the client (so we can raise ERROR_ARG instead of ERROR_SYSTEM). + # Maximum accepted arguments: argspec includes "self". + max_args = len(argspec.args) - 1 - extra_args + # Minimum accepted arguments: some arguments might be optional. + min_args = max_args + if argspec.defaults: + min_args -= len(argspec.defaults) + wrong_num = (len(self.args) > max_args) or (len(self.args) < min_args) + # If the command accepts a variable number of arguments skip the check. + if wrong_num and not argspec.varargs: + raise TypeError('wrong number of arguments for "{}"' + .format(self.name), self.name) + + return func + def run(self, conn): """A coroutine that executes the command on the given connection. """ - # Attempt to get correct command function. - func_name = 'cmd_' + self.name - if not hasattr(conn.server, func_name): - raise BPDError(ERROR_UNKNOWN, u'unknown command', self.name) - func = getattr(conn.server, func_name) + try: + # `conn` is an extra argument to all cmd handlers. + func = self.delegate('cmd_', conn.server, extra_args=1) + except AttributeError as e: + raise BPDError(ERROR_UNKNOWN, e.args[0]) + except TypeError as e: + raise BPDError(ERROR_ARG, e.args[0], self.name) # Ensure we have permission for this command. if conn.server.password and \ not conn.authenticated and \ self.name not in SAFE_COMMANDS: - raise BPDError(ERROR_PERMISSION, u'insufficient privileges') + raise BPDError(ERROR_PERMISSION, 'insufficient privileges') try: args = [conn] + self.args @@ -697,10 +1035,13 @@ class Command(object): # it on the Connection. raise - except Exception as e: + except BPDIdle: + raise + + except Exception: # An "unintentional" error. Hide it from the client. - log.error('{}', traceback.format_exc(e)) - raise BPDError(ERROR_SYSTEM, u'server error', self.name) + conn.server._log.error('{}', traceback.format_exc()) + raise BPDError(ERROR_SYSTEM, 'server error', self.name) class CommandList(list): @@ -729,7 +1070,7 @@ class CommandList(list): e.index = i # Give the error the correct index. raise e - # Otherwise, possibly send the output delimeter if we're in a + # Otherwise, possibly send the output delimiter if we're in a # verbose ("OK") command list. if self.verbose: yield conn.send(RESP_CLIST_VERBOSE) @@ -743,7 +1084,7 @@ class Server(BaseServer): to store its library. """ - def __init__(self, library, host, port, password): + def __init__(self, library, host, port, password, ctrl_port, log): try: from beetsplug.bpd import gstplayer except ImportError as e: @@ -752,65 +1093,80 @@ class Server(BaseServer): raise NoGstreamerError() else: raise - super(Server, self).__init__(host, port, password) + log.info('Starting server...') + super().__init__(host, port, password, ctrl_port, log) self.lib = library self.player = gstplayer.GstPlayer(self.play_finished) self.cmd_update(None) + log.info('Server ready and listening on {}:{}'.format( + host, port)) + log.debug('Listening for control signals on {}:{}'.format( + host, ctrl_port)) def run(self): self.player.run() - super(Server, self).run() + super().run() def play_finished(self): - """A callback invoked every time our player finishes a - track. + """A callback invoked every time our player finishes a track. """ self.cmd_next(None) + self._ctrl_send('play_finished') # Metadata helper functions. def _item_info(self, item): info_lines = [ - u'file: ' + item.destination(fragment=True), - u'Time: ' + six.text_type(int(item.length)), - u'Title: ' + item.title, - u'Artist: ' + item.artist, - u'Album: ' + item.album, - u'Genre: ' + item.genre, + 'file: ' + item.destination(fragment=True), + 'Time: ' + str(int(item.length)), + 'duration: ' + f'{item.length:.3f}', + 'Id: ' + str(item.id), ] - track = six.text_type(item.track) - if item.tracktotal: - track += u'/' + six.text_type(item.tracktotal) - info_lines.append(u'Track: ' + track) - - info_lines.append(u'Date: ' + six.text_type(item.year)) - try: pos = self._id_to_index(item.id) - info_lines.append(u'Pos: ' + six.text_type(pos)) + info_lines.append('Pos: ' + str(pos)) except ArgumentNotFoundError: # Don't include position if not in playlist. pass - info_lines.append(u'Id: ' + six.text_type(item.id)) + for tagtype, field in self.tagtype_map.items(): + info_lines.append('{}: {}'.format( + tagtype, str(getattr(item, field)))) return info_lines + def _parse_range(self, items, accept_single_number=False): + """Convert a range of positions to a list of item info. + MPD specifies ranges as START:STOP (endpoint excluded) for some + commands. Sometimes a single number can be provided instead. + """ + try: + start, stop = str(items).split(':', 1) + except ValueError: + if accept_single_number: + return [cast_arg(int, items)] + raise BPDError(ERROR_ARG, 'bad range syntax') + start = cast_arg(int, start) + stop = cast_arg(int, stop) + return range(start, stop) + def _item_id(self, item): return item.id # Database updating. - def cmd_update(self, conn, path=u'/'): + def cmd_update(self, conn, path='/'): """Updates the catalog to reflect the current database state. """ # Path is ignored. Also, the real MPD does this asynchronously; # this is done inline. - print(u'Building directory tree...') + self._log.debug('Building directory tree...') self.tree = vfs.libtree(self.lib) - print(u'... done.') + self._log.debug('Finished building directory tree.') self.updated_time = time.time() + self._send_event('update') + self._send_event('database') # Path (directory tree) browsing. @@ -818,7 +1174,7 @@ class Server(BaseServer): """Returns a VFS node or an item ID located at the path given. If the path does not exist, raises a """ - components = path.split(u'/') + components = path.split('/') node = self.tree for component in components: @@ -840,25 +1196,25 @@ class Server(BaseServer): def _path_join(self, p1, p2): """Smashes together two BPD paths.""" - out = p1 + u'/' + p2 - return out.replace(u'//', u'/').replace(u'//', u'/') + out = p1 + '/' + p2 + return out.replace('//', '/').replace('//', '/') - def cmd_lsinfo(self, conn, path=u"/"): + def cmd_lsinfo(self, conn, path="/"): """Sends info on all the items in the path.""" node = self._resolve_path(path) if isinstance(node, int): # Trying to list a track. - raise BPDError(ERROR_ARG, u'this is not a directory') + raise BPDError(ERROR_ARG, 'this is not a directory') else: for name, itemid in iter(sorted(node.files.items())): item = self.lib.get_item(itemid) yield self._item_info(item) for name, _ in iter(sorted(node.dirs.items())): dirpath = self._path_join(path, name) - if dirpath.startswith(u"/"): + if dirpath.startswith("/"): # Strip leading slash (libmpc rejects this). dirpath = dirpath[1:] - yield u'directory: %s' % dirpath + yield 'directory: %s' % dirpath def _listall(self, basepath, node, info=False): """Helper function for recursive listing. If info, show @@ -870,25 +1226,23 @@ class Server(BaseServer): item = self.lib.get_item(node) yield self._item_info(item) else: - yield u'file: ' + basepath + yield 'file: ' + basepath else: # List a directory. Recurse into both directories and files. for name, itemid in sorted(node.files.items()): newpath = self._path_join(basepath, name) # "yield from" - for v in self._listall(newpath, itemid, info): - yield v + yield from self._listall(newpath, itemid, info) for name, subdir in sorted(node.dirs.items()): newpath = self._path_join(basepath, name) - yield u'directory: ' + newpath - for v in self._listall(newpath, subdir, info): - yield v + yield 'directory: ' + newpath + yield from self._listall(newpath, subdir, info) - def cmd_listall(self, conn, path=u"/"): + def cmd_listall(self, conn, path="/"): """Send the paths all items in the directory, recursively.""" return self._listall(path, self._resolve_path(path), False) - def cmd_listallinfo(self, conn, path=u"/"): + def cmd_listallinfo(self, conn, path="/"): """Send info on all the items in the directory, recursively.""" return self._listall(path, self._resolve_path(path), True) @@ -905,11 +1259,9 @@ class Server(BaseServer): # Recurse into a directory. for name, itemid in sorted(node.files.items()): # "yield from" - for v in self._all_items(itemid): - yield v + yield from self._all_items(itemid) for name, subdir in sorted(node.dirs.items()): - for v in self._all_items(subdir): - yield v + yield from self._all_items(subdir) def _add(self, path, send_id=False): """Adds a track or directory to the playlist, specified by the @@ -918,8 +1270,9 @@ class Server(BaseServer): for item in self._all_items(self._resolve_path(path)): self.playlist.append(item) if send_id: - yield u'Id: ' + six.text_type(item.id) + yield 'Id: ' + str(item.id) self.playlist_version += 1 + self._send_event('playlist') def cmd_add(self, conn, path): """Adds a track or directory to the playlist, specified by a @@ -934,16 +1287,28 @@ class Server(BaseServer): # Server info. def cmd_status(self, conn): - for line in super(Server, self).cmd_status(conn): - yield line + yield from super().cmd_status(conn) if self.current_index > -1: item = self.playlist[self.current_index] - yield u'bitrate: ' + six.text_type(item.bitrate / 1000) - # Missing 'audio'. + yield ( + 'bitrate: ' + str(item.bitrate / 1000), + 'audio: {}:{}:{}'.format( + str(item.samplerate), + str(item.bitdepth), + str(item.channels), + ), + ) (pos, total) = self.player.time() - yield u'time: ' + six.text_type(pos) + u':' + six.text_type(total) + yield ( + 'time: {}:{}'.format( + str(int(pos)), + str(int(total)), + ), + 'elapsed: ' + f'{pos:.3f}', + 'duration: ' + f'{total:.3f}', + ) # Also missing 'updating_db'. @@ -958,31 +1323,47 @@ class Server(BaseServer): artists, albums, songs, totaltime = tx.query(statement)[0] yield ( - u'artists: ' + six.text_type(artists), - u'albums: ' + six.text_type(albums), - u'songs: ' + six.text_type(songs), - u'uptime: ' + six.text_type(int(time.time() - self.startup_time)), - u'playtime: ' + u'0', # Missing. - u'db_playtime: ' + six.text_type(int(totaltime)), - u'db_update: ' + six.text_type(int(self.updated_time)), + 'artists: ' + str(artists), + 'albums: ' + str(albums), + 'songs: ' + str(songs), + 'uptime: ' + str(int(time.time() - self.startup_time)), + 'playtime: ' + '0', # Missing. + 'db_playtime: ' + str(int(totaltime)), + 'db_update: ' + str(int(self.updated_time)), ) + def cmd_decoders(self, conn): + """Send list of supported decoders and formats.""" + decoders = self.player.get_decoders() + for name, (mimes, exts) in decoders.items(): + yield f'plugin: {name}' + for ext in exts: + yield f'suffix: {ext}' + for mime in mimes: + yield f'mime_type: {mime}' + # Searching. tagtype_map = { - u'Artist': u'artist', - u'Album': u'album', - u'Title': u'title', - u'Track': u'track', - u'AlbumArtist': u'albumartist', - u'AlbumArtistSort': u'albumartist_sort', - # Name? - u'Genre': u'genre', - u'Date': u'year', - u'Composer': u'composer', - # Performer? - u'Disc': u'disc', - u'filename': u'path', # Suspect. + 'Artist': 'artist', + 'ArtistSort': 'artist_sort', + 'Album': 'album', + 'Title': 'title', + 'Track': 'track', + 'AlbumArtist': 'albumartist', + 'AlbumArtistSort': 'albumartist_sort', + 'Label': 'label', + 'Genre': 'genre', + 'Date': 'year', + 'OriginalDate': 'original_year', + 'Composer': 'composer', + 'Disc': 'disc', + 'Comment': 'comments', + 'MUSICBRAINZ_TRACKID': 'mb_trackid', + 'MUSICBRAINZ_ALBUMID': 'mb_albumid', + 'MUSICBRAINZ_ARTISTID': 'mb_artistid', + 'MUSICBRAINZ_ALBUMARTISTID': 'mb_albumartistid', + 'MUSICBRAINZ_RELEASETRACKID': 'mb_releasetrackid', } def cmd_tagtypes(self, conn): @@ -990,7 +1371,7 @@ class Server(BaseServer): searching. """ for tag in self.tagtype_map: - yield u'tagtype: ' + tag + yield 'tagtype: ' + tag def _tagtype_lookup(self, tag): """Uses `tagtype_map` to look up the beets column name for an @@ -1002,7 +1383,7 @@ class Server(BaseServer): # Match case-insensitively. if test_tag.lower() == tag.lower(): return test_tag, key - raise BPDError(ERROR_UNKNOWN, u'no such tagtype') + raise BPDError(ERROR_UNKNOWN, 'no such tagtype') def _metadata_query(self, query_type, any_query_type, kv): """Helper function returns a query object that will find items @@ -1015,13 +1396,13 @@ class Server(BaseServer): # Iterate pairwise over the arguments. it = iter(kv) for tag, value in zip(it, it): - if tag.lower() == u'any': + if tag.lower() == 'any': if any_query_type: queries.append(any_query_type(value, ITEM_KEYS_WRITABLE, query_type)) else: - raise BPDError(ERROR_UNKNOWN, u'no such tagtype') + raise BPDError(ERROR_UNKNOWN, 'no such tagtype') else: _, key = self._tagtype_lookup(tag) queries.append(query_type(key, value)) @@ -1050,17 +1431,32 @@ class Server(BaseServer): filtered by matching match_tag to match_term. """ show_tag_canon, show_key = self._tagtype_lookup(show_tag) + if len(kv) == 1: + if show_tag_canon == 'Album': + # If no tag was given, assume artist. This is because MPD + # supports a short version of this command for fetching the + # albums belonging to a particular artist, and some clients + # rely on this behaviour (e.g. MPDroid, M.A.L.P.). + kv = ('Artist', kv[0]) + else: + raise BPDError(ERROR_ARG, 'should be "Album" for 3 arguments') + elif len(kv) % 2 != 0: + raise BPDError(ERROR_ARG, 'Incorrect number of filter arguments') query = self._metadata_query(dbcore.query.MatchQuery, None, kv) clause, subvals = query.clause() statement = 'SELECT DISTINCT ' + show_key + \ ' FROM items WHERE ' + clause + \ ' ORDER BY ' + show_key + self._log.debug(statement) with self.lib.transaction() as tx: rows = tx.query(statement, subvals) for row in rows: - yield show_tag_canon + u': ' + six.text_type(row[0]) + if not row[0]: + # Skip any empty values of the field. + continue + yield show_tag_canon + ': ' + str(row[0]) def cmd_count(self, conn, tag, value): """Returns the number and total time of songs matching the @@ -1072,8 +1468,44 @@ class Server(BaseServer): for item in self.lib.items(dbcore.query.MatchQuery(key, value)): songs += 1 playtime += item.length - yield u'songs: ' + six.text_type(songs) - yield u'playtime: ' + six.text_type(int(playtime)) + yield 'songs: ' + str(songs) + yield 'playtime: ' + str(int(playtime)) + + # Persistent playlist manipulation. In MPD this is an optional feature so + # these dummy implementations match MPD's behaviour with the feature off. + + def cmd_listplaylist(self, conn, playlist): + raise BPDError(ERROR_NO_EXIST, 'No such playlist') + + def cmd_listplaylistinfo(self, conn, playlist): + raise BPDError(ERROR_NO_EXIST, 'No such playlist') + + def cmd_listplaylists(self, conn): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_load(self, conn, playlist): + raise BPDError(ERROR_NO_EXIST, 'Stored playlists are disabled') + + def cmd_playlistadd(self, conn, playlist, uri): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_playlistclear(self, conn, playlist): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_playlistdelete(self, conn, playlist, index): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_playlistmove(self, conn, playlist, from_index, to_index): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_rename(self, conn, playlist, new_name): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_rm(self, conn, playlist): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') + + def cmd_save(self, conn, playlist): + raise BPDError(ERROR_UNKNOWN, 'Stored playlists are disabled') # "Outputs." Just a dummy implementation because we don't control # any outputs. @@ -1081,9 +1513,9 @@ class Server(BaseServer): def cmd_outputs(self, conn): """List the available outputs.""" yield ( - u'outputid: 0', - u'outputname: gstreamer', - u'outputenabled: 1', + 'outputid: 0', + 'outputname: gstreamer', + 'outputenabled: 1', ) def cmd_enableoutput(self, conn, output_id): @@ -1094,7 +1526,7 @@ class Server(BaseServer): def cmd_disableoutput(self, conn, output_id): output_id = cast_arg(int, output_id) if output_id == 0: - raise BPDError(ERROR_ARG, u'cannot disable this output') + raise BPDError(ERROR_ARG, 'cannot disable this output') else: raise ArgumentIndexError() @@ -1105,7 +1537,7 @@ class Server(BaseServer): def cmd_play(self, conn, index=-1): new_index = index != -1 and index != self.current_index was_paused = self.paused - super(Server, self).cmd_play(conn, index) + super().cmd_play(conn, index) if self.current_index > -1: # Not stopped. if was_paused and not new_index: @@ -1115,28 +1547,28 @@ class Server(BaseServer): self.player.play_file(self.playlist[self.current_index].path) def cmd_pause(self, conn, state=None): - super(Server, self).cmd_pause(conn, state) + super().cmd_pause(conn, state) if self.paused: self.player.pause() elif self.player.playing: self.player.play() def cmd_stop(self, conn): - super(Server, self).cmd_stop(conn) + super().cmd_stop(conn) self.player.stop() def cmd_seek(self, conn, index, pos): """Seeks to the specified position in the specified song.""" index = cast_arg(int, index) - pos = cast_arg(int, pos) - super(Server, self).cmd_seek(conn, index, pos) + pos = cast_arg(float, pos) + super().cmd_seek(conn, index, pos) self.player.seek(pos) # Volume control. def cmd_setvol(self, conn, vol): vol = cast_arg(int, vol) - super(Server, self).cmd_setvol(conn, vol) + super().cmd_setvol(conn, vol) self.player.volume = float(vol) / 100 @@ -1147,37 +1579,30 @@ class BPDPlugin(BeetsPlugin): server. """ def __init__(self): - super(BPDPlugin, self).__init__() + super().__init__() self.config.add({ - 'host': u'', + 'host': '', 'port': 6600, - 'password': u'', + 'control_port': 6601, + 'password': '', 'volume': VOLUME_MAX, }) self.config['password'].redact = True - def start_bpd(self, lib, host, port, password, volume, debug): + def start_bpd(self, lib, host, port, password, volume, ctrl_port): """Starts a BPD server.""" - if debug: # FIXME this should be managed by BeetsPlugin - self._log.setLevel(logging.DEBUG) - else: - self._log.setLevel(logging.WARNING) try: - server = Server(lib, host, port, password) + server = Server(lib, host, port, password, ctrl_port, self._log) server.cmd_setvol(None, volume) server.run() except NoGstreamerError: - global_log.error(u'Gstreamer Python bindings not found.') - global_log.error(u'Install "gstreamer1.0" and "python-gi"' - u'or similar package to use BPD.') + self._log.error('Gstreamer Python bindings not found.') + self._log.error('Install "gstreamer1.0" and "python-gi"' + 'or similar package to use BPD.') def commands(self): cmd = beets.ui.Subcommand( - 'bpd', help=u'run an MPD-compatible music player server' - ) - cmd.parser.add_option( - '-d', '--debug', action='store_true', - help=u'dump all MPD traffic to stdout' + 'bpd', help='run an MPD-compatible music player server' ) def func(lib, opts, args): @@ -1185,11 +1610,15 @@ class BPDPlugin(BeetsPlugin): host = args.pop(0) if args else host port = args.pop(0) if args else self.config['port'].get(int) if args: - raise beets.ui.UserError(u'too many arguments') + ctrl_port = args.pop(0) + else: + ctrl_port = self.config['control_port'].get(int) + if args: + raise beets.ui.UserError('too many arguments') password = self.config['password'].as_str() volume = self.config['volume'].get(int) - debug = opts.debug or False - self.start_bpd(lib, host, int(port), password, volume, debug) + self.start_bpd(lib, host, int(port), password, volume, + int(ctrl_port)) cmd.func = func return [cmd] diff --git a/libs/common/beetsplug/bpd/gstplayer.py b/libs/common/beetsplug/bpd/gstplayer.py index 705692aa..64954b1c 100644 --- a/libs/common/beetsplug/bpd/gstplayer.py +++ b/libs/common/beetsplug/bpd/gstplayer.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -17,15 +16,13 @@ music player. """ -from __future__ import division, absolute_import, print_function -import six import sys import time -from six.moves import _thread +import _thread import os import copy -from six.moves import urllib +import urllib from beets import ui import gi @@ -40,7 +37,7 @@ class QueryError(Exception): pass -class GstPlayer(object): +class GstPlayer: """A music player abstracting GStreamer's Playbin element. Create a player object, then call run() to start a thread with a @@ -64,7 +61,8 @@ class GstPlayer(object): """ # Set up the Gstreamer player. From the pygst tutorial: - # http://pygstdocs.berlios.de/pygst-tutorial/playbin.html + # https://pygstdocs.berlios.de/pygst-tutorial/playbin.html (gone) + # https://brettviren.github.io/pygst-tutorial-org/pygst-tutorial.html #### # Updated to GStreamer 1.0 with: # https://wiki.ubuntu.com/Novacut/GStreamer1.0 @@ -109,7 +107,7 @@ class GstPlayer(object): # error self.player.set_state(Gst.State.NULL) err, debug = message.parse_error() - print(u"Error: {0}".format(err)) + print(f"Error: {err}") self.playing = False def _set_volume(self, volume): @@ -129,7 +127,7 @@ class GstPlayer(object): path. """ self.player.set_state(Gst.State.NULL) - if isinstance(path, six.text_type): + if isinstance(path, str): path = path.encode('utf-8') uri = 'file://' + urllib.parse.quote(path) self.player.set_property("uri", uri) @@ -177,12 +175,12 @@ class GstPlayer(object): posq = self.player.query_position(fmt) if not posq[0]: raise QueryError("query_position failed") - pos = posq[1] // (10 ** 9) + pos = posq[1] / (10 ** 9) lengthq = self.player.query_duration(fmt) if not lengthq[0]: raise QueryError("query_duration failed") - length = lengthq[1] // (10 ** 9) + length = lengthq[1] / (10 ** 9) self.cached_time = (pos, length) return (pos, length) @@ -215,6 +213,59 @@ class GstPlayer(object): while self.playing: time.sleep(1) + def get_decoders(self): + return get_decoders() + + +def get_decoders(): + """Get supported audio decoders from GStreamer. + Returns a dict mapping decoder element names to the associated media types + and file extensions. + """ + # We only care about audio decoder elements. + filt = (Gst.ELEMENT_FACTORY_TYPE_DEPAYLOADER | + Gst.ELEMENT_FACTORY_TYPE_DEMUXER | + Gst.ELEMENT_FACTORY_TYPE_PARSER | + Gst.ELEMENT_FACTORY_TYPE_DECODER | + Gst.ELEMENT_FACTORY_TYPE_MEDIA_AUDIO) + + decoders = {} + mime_types = set() + for f in Gst.ElementFactory.list_get_elements(filt, Gst.Rank.NONE): + for pad in f.get_static_pad_templates(): + if pad.direction == Gst.PadDirection.SINK: + caps = pad.static_caps.get() + mimes = set() + for i in range(caps.get_size()): + struct = caps.get_structure(i) + mime = struct.get_name() + if mime == 'unknown/unknown': + continue + mimes.add(mime) + mime_types.add(mime) + if mimes: + decoders[f.get_name()] = (mimes, set()) + + # Check all the TypeFindFactory plugin features form the registry. If they + # are associated with an audio media type that we found above, get the list + # of corresponding file extensions. + mime_extensions = {mime: set() for mime in mime_types} + for feat in Gst.Registry.get().get_feature_list(Gst.TypeFindFactory): + caps = feat.get_caps() + if caps: + for i in range(caps.get_size()): + struct = caps.get_structure(i) + mime = struct.get_name() + if mime in mime_types: + mime_extensions[mime].update(feat.get_extensions()) + + # Fill in the slot we left for file extensions. + for name, (mimes, exts) in decoders.items(): + for mime in mimes: + exts.update(mime_extensions[mime]) + + return decoders + def play_simple(paths): """Play the files in paths in a straightforward way, without diff --git a/libs/common/beetsplug/bpm.py b/libs/common/beetsplug/bpm.py index 20218bd3..5aa2d95a 100644 --- a/libs/common/beetsplug/bpm.py +++ b/libs/common/beetsplug/bpm.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, aroquen # @@ -15,10 +14,8 @@ """Determine BPM by pressing a key to the rhythm.""" -from __future__ import division, absolute_import, print_function import time -from six.moves import input from beets import ui from beets.plugins import BeetsPlugin @@ -51,16 +48,16 @@ def bpm(max_strokes): class BPMPlugin(BeetsPlugin): def __init__(self): - super(BPMPlugin, self).__init__() + super().__init__() self.config.add({ - u'max_strokes': 3, - u'overwrite': True, + 'max_strokes': 3, + 'overwrite': True, }) def commands(self): cmd = ui.Subcommand('bpm', - help=u'determine bpm of a song by pressing ' - u'a key to the rhythm') + help='determine bpm of a song by pressing ' + 'a key to the rhythm') cmd.func = self.command return [cmd] @@ -72,19 +69,19 @@ class BPMPlugin(BeetsPlugin): def get_bpm(self, items, write=False): overwrite = self.config['overwrite'].get(bool) if len(items) > 1: - raise ValueError(u'Can only get bpm of one song at time') + raise ValueError('Can only get bpm of one song at time') item = items[0] if item['bpm']: - self._log.info(u'Found bpm {0}', item['bpm']) + self._log.info('Found bpm {0}', item['bpm']) if not overwrite: return - self._log.info(u'Press Enter {0} times to the rhythm or Ctrl-D ' - u'to exit', self.config['max_strokes'].get(int)) + self._log.info('Press Enter {0} times to the rhythm or Ctrl-D ' + 'to exit', self.config['max_strokes'].get(int)) new_bpm = bpm(self.config['max_strokes'].get(int)) item['bpm'] = int(new_bpm) if write: item.try_write() item.store() - self._log.info(u'Added new bpm {0}', item['bpm']) + self._log.info('Added new bpm {0}', item['bpm']) diff --git a/libs/common/beetsplug/bpsync.py b/libs/common/beetsplug/bpsync.py new file mode 100644 index 00000000..5b28d6d2 --- /dev/null +++ b/libs/common/beetsplug/bpsync.py @@ -0,0 +1,186 @@ +# This file is part of beets. +# Copyright 2019, Rahul Ahuja. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Update library's tags using Beatport. +""" + +from beets.plugins import BeetsPlugin, apply_item_changes +from beets import autotag, library, ui, util + +from .beatport import BeatportPlugin + + +class BPSyncPlugin(BeetsPlugin): + def __init__(self): + super().__init__() + self.beatport_plugin = BeatportPlugin() + self.beatport_plugin.setup() + + def commands(self): + cmd = ui.Subcommand('bpsync', help='update metadata from Beatport') + cmd.parser.add_option( + '-p', + '--pretend', + action='store_true', + help='show all changes but do nothing', + ) + cmd.parser.add_option( + '-m', + '--move', + action='store_true', + dest='move', + help="move files in the library directory", + ) + cmd.parser.add_option( + '-M', + '--nomove', + action='store_false', + dest='move', + help="don't move files in library", + ) + cmd.parser.add_option( + '-W', + '--nowrite', + action='store_false', + default=None, + dest='write', + help="don't write updated metadata to files", + ) + cmd.parser.add_format_option() + cmd.func = self.func + return [cmd] + + def func(self, lib, opts, args): + """Command handler for the bpsync function. + """ + move = ui.should_move(opts.move) + pretend = opts.pretend + write = ui.should_write(opts.write) + query = ui.decargs(args) + + self.singletons(lib, query, move, pretend, write) + self.albums(lib, query, move, pretend, write) + + def singletons(self, lib, query, move, pretend, write): + """Retrieve and apply info from the autotagger for items matched by + query. + """ + for item in lib.items(query + ['singleton:true']): + if not item.mb_trackid: + self._log.info( + 'Skipping singleton with no mb_trackid: {}', item + ) + continue + + if not self.is_beatport_track(item): + self._log.info( + 'Skipping non-{} singleton: {}', + self.beatport_plugin.data_source, + item, + ) + continue + + # Apply. + trackinfo = self.beatport_plugin.track_for_id(item.mb_trackid) + with lib.transaction(): + autotag.apply_item_metadata(item, trackinfo) + apply_item_changes(lib, item, move, pretend, write) + + @staticmethod + def is_beatport_track(item): + return ( + item.get('data_source') == BeatportPlugin.data_source + and item.mb_trackid.isnumeric() + ) + + def get_album_tracks(self, album): + if not album.mb_albumid: + self._log.info('Skipping album with no mb_albumid: {}', album) + return False + if not album.mb_albumid.isnumeric(): + self._log.info( + 'Skipping album with invalid {} ID: {}', + self.beatport_plugin.data_source, + album, + ) + return False + items = list(album.items()) + if album.get('data_source') == self.beatport_plugin.data_source: + return items + if not all(self.is_beatport_track(item) for item in items): + self._log.info( + 'Skipping non-{} release: {}', + self.beatport_plugin.data_source, + album, + ) + return False + return items + + def albums(self, lib, query, move, pretend, write): + """Retrieve and apply info from the autotagger for albums matched by + query and their items. + """ + # Process matching albums. + for album in lib.albums(query): + # Do we have a valid Beatport album? + items = self.get_album_tracks(album) + if not items: + continue + + # Get the Beatport album information. + albuminfo = self.beatport_plugin.album_for_id(album.mb_albumid) + if not albuminfo: + self._log.info( + 'Release ID {} not found for album {}', + album.mb_albumid, + album, + ) + continue + + beatport_trackid_to_trackinfo = { + track.track_id: track for track in albuminfo.tracks + } + library_trackid_to_item = { + int(item.mb_trackid): item for item in items + } + item_to_trackinfo = { + item: beatport_trackid_to_trackinfo[track_id] + for track_id, item in library_trackid_to_item.items() + } + + self._log.info('applying changes to {}', album) + with lib.transaction(): + autotag.apply_metadata(albuminfo, item_to_trackinfo) + changed = False + # Find any changed item to apply Beatport changes to album. + any_changed_item = items[0] + for item in items: + item_changed = ui.show_model_changes(item) + changed |= item_changed + if item_changed: + any_changed_item = item + apply_item_changes(lib, item, move, pretend, write) + + if pretend or not changed: + continue + + # Update album structure to reflect an item in it. + for key in library.Album.item_keys: + album[key] = any_changed_item[key] + album.store() + + # Move album art (and any inconsistent items). + if move and lib.directory in util.ancestry(items[0].path): + self._log.debug('moving album {}', album) + album.move() diff --git a/libs/common/beetsplug/bucket.py b/libs/common/beetsplug/bucket.py index c4be2a3d..9ed50b45 100644 --- a/libs/common/beetsplug/bucket.py +++ b/libs/common/beetsplug/bucket.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Fabrice Laporte. # @@ -16,12 +15,10 @@ """Provides the %bucket{} function for path formatting. """ -from __future__ import division, absolute_import, print_function from datetime import datetime import re import string -from six.moves import zip from itertools import tee from beets import plugins, ui @@ -49,7 +46,7 @@ def span_from_str(span_str): """Convert string to a 4 digits year """ if yearfrom < 100: - raise BucketError(u"%d must be expressed on 4 digits" % yearfrom) + raise BucketError("%d must be expressed on 4 digits" % yearfrom) # if two digits only, pick closest year that ends by these two # digits starting from yearfrom @@ -60,14 +57,14 @@ def span_from_str(span_str): d = (yearfrom - yearfrom % 100) + d return d - years = [int(x) for x in re.findall('\d+', span_str)] + years = [int(x) for x in re.findall(r'\d+', span_str)] if not years: - raise ui.UserError(u"invalid range defined for year bucket '%s': no " - u"year found" % span_str) + raise ui.UserError("invalid range defined for year bucket '%s': no " + "year found" % span_str) try: years = [normalize_year(x, years[0]) for x in years] except BucketError as exc: - raise ui.UserError(u"invalid range defined for year bucket '%s': %s" % + raise ui.UserError("invalid range defined for year bucket '%s': %s" % (span_str, exc)) res = {'from': years[0], 'str': span_str} @@ -128,10 +125,10 @@ def str2fmt(s): res = {'fromnchars': len(m.group('fromyear')), 'tonchars': len(m.group('toyear'))} - res['fmt'] = "%s%%s%s%s%s" % (m.group('bef'), - m.group('sep'), - '%s' if res['tonchars'] else '', - m.group('after')) + res['fmt'] = "{}%s{}{}{}".format(m.group('bef'), + m.group('sep'), + '%s' if res['tonchars'] else '', + m.group('after')) return res @@ -170,8 +167,8 @@ def build_alpha_spans(alpha_spans_str, alpha_regexs): begin_index = ASCII_DIGITS.index(bucket[0]) end_index = ASCII_DIGITS.index(bucket[-1]) else: - raise ui.UserError(u"invalid range defined for alpha bucket " - u"'%s': no alphanumeric character found" % + raise ui.UserError("invalid range defined for alpha bucket " + "'%s': no alphanumeric character found" % elem) spans.append( re.compile( @@ -184,7 +181,7 @@ def build_alpha_spans(alpha_spans_str, alpha_regexs): class BucketPlugin(plugins.BeetsPlugin): def __init__(self): - super(BucketPlugin, self).__init__() + super().__init__() self.template_funcs['bucket'] = self._tmpl_bucket self.config.add({ diff --git a/libs/common/beetsplug/chroma.py b/libs/common/beetsplug/chroma.py index 57472956..353923aa 100644 --- a/libs/common/beetsplug/chroma.py +++ b/libs/common/beetsplug/chroma.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,16 +15,17 @@ """Adds Chromaprint/Acoustid acoustic fingerprinting support to the autotagger. Requires the pyacoustid library. """ -from __future__ import division, absolute_import, print_function from beets import plugins from beets import ui from beets import util from beets import config -from beets.util import confit from beets.autotag import hooks +import confuse import acoustid from collections import defaultdict +from functools import partial +import re API_KEY = '1vOwZtEn' SCORE_THRESH = 0.5 @@ -57,6 +57,30 @@ def prefix(it, count): yield v +def releases_key(release, countries, original_year): + """Used as a key to sort releases by date then preferred country + """ + date = release.get('date') + if date and original_year: + year = date.get('year', 9999) + month = date.get('month', 99) + day = date.get('day', 99) + else: + year = 9999 + month = 99 + day = 99 + + # Uses index of preferred countries to sort + country_key = 99 + if release.get('country'): + for i, country in enumerate(countries): + if country.match(release['country']): + country_key = i + break + + return (year, month, day, country_key) + + def acoustid_match(log, path): """Gets metadata for a file from Acoustid and populates the _matches, _fingerprints, and _acoustids dictionaries accordingly. @@ -64,42 +88,55 @@ def acoustid_match(log, path): try: duration, fp = acoustid.fingerprint_file(util.syspath(path)) except acoustid.FingerprintGenerationError as exc: - log.error(u'fingerprinting of {0} failed: {1}', + log.error('fingerprinting of {0} failed: {1}', util.displayable_path(repr(path)), exc) return None + fp = fp.decode() _fingerprints[path] = fp try: res = acoustid.lookup(API_KEY, fp, duration, meta='recordings releases') except acoustid.AcoustidError as exc: - log.debug(u'fingerprint matching {0} failed: {1}', + log.debug('fingerprint matching {0} failed: {1}', util.displayable_path(repr(path)), exc) return None - log.debug(u'chroma: fingerprinted {0}', + log.debug('chroma: fingerprinted {0}', util.displayable_path(repr(path))) # Ensure the response is usable and parse it. if res['status'] != 'ok' or not res.get('results'): - log.debug(u'no match found') + log.debug('no match found') return None result = res['results'][0] # Best match. if result['score'] < SCORE_THRESH: - log.debug(u'no results above threshold') + log.debug('no results above threshold') return None _acoustids[path] = result['id'] - # Get recording and releases from the result. + # Get recording and releases from the result if not result.get('recordings'): - log.debug(u'no recordings found') + log.debug('no recordings found') return None recording_ids = [] - release_ids = [] + releases = [] for recording in result['recordings']: recording_ids.append(recording['id']) if 'releases' in recording: - release_ids += [rel['id'] for rel in recording['releases']] + releases.extend(recording['releases']) - log.debug(u'matched recordings {0} on releases {1}', + # The releases list is essentially in random order from the Acoustid lookup + # so we optionally sort it using the match.preferred configuration options. + # 'original_year' to sort the earliest first and + # 'countries' to then sort preferred countries first. + country_patterns = config['match']['preferred']['countries'].as_str_seq() + countries = [re.compile(pat, re.I) for pat in country_patterns] + original_year = config['match']['preferred']['original_year'] + releases.sort(key=partial(releases_key, + countries=countries, + original_year=original_year)) + release_ids = [rel['id'] for rel in releases] + + log.debug('matched recordings {0} on releases {1}', recording_ids, release_ids) _matches[path] = recording_ids, release_ids @@ -128,7 +165,7 @@ def _all_releases(items): class AcoustidPlugin(plugins.BeetsPlugin): def __init__(self): - super(AcoustidPlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, @@ -152,14 +189,14 @@ class AcoustidPlugin(plugins.BeetsPlugin): dist.add_expr('track_id', info.track_id not in recording_ids) return dist - def candidates(self, items, artist, album, va_likely): + def candidates(self, items, artist, album, va_likely, extra_tags=None): albums = [] for relid in prefix(_all_releases(items), MAX_RELEASES): album = hooks.album_for_mbid(relid) if album: albums.append(album) - self._log.debug(u'acoustid album candidates: {0}', len(albums)) + self._log.debug('acoustid album candidates: {0}', len(albums)) return albums def item_candidates(self, item, artist, title): @@ -172,24 +209,24 @@ class AcoustidPlugin(plugins.BeetsPlugin): track = hooks.track_for_mbid(recording_id) if track: tracks.append(track) - self._log.debug(u'acoustid item candidates: {0}', len(tracks)) + self._log.debug('acoustid item candidates: {0}', len(tracks)) return tracks def commands(self): submit_cmd = ui.Subcommand('submit', - help=u'submit Acoustid fingerprints') + help='submit Acoustid fingerprints') def submit_cmd_func(lib, opts, args): try: apikey = config['acoustid']['apikey'].as_str() - except confit.NotFoundError: - raise ui.UserError(u'no Acoustid user API key provided') + except confuse.NotFoundError: + raise ui.UserError('no Acoustid user API key provided') submit_items(self._log, apikey, lib.items(ui.decargs(args))) submit_cmd.func = submit_cmd_func fingerprint_cmd = ui.Subcommand( 'fingerprint', - help=u'generate fingerprints for items without them' + help='generate fingerprints for items without them' ) def fingerprint_cmd_func(lib, opts, args): @@ -232,15 +269,15 @@ def submit_items(log, userkey, items, chunksize=64): def submit_chunk(): """Submit the current accumulated fingerprint data.""" - log.info(u'submitting {0} fingerprints', len(data)) + log.info('submitting {0} fingerprints', len(data)) try: acoustid.submit(API_KEY, userkey, data) except acoustid.AcoustidError as exc: - log.warning(u'acoustid submission error: {0}', exc) + log.warning('acoustid submission error: {0}', exc) del data[:] for item in items: - fp = fingerprint_item(log, item) + fp = fingerprint_item(log, item, write=ui.should_write()) # Construct a submission dictionary for this item. item_data = { @@ -249,7 +286,7 @@ def submit_items(log, userkey, items, chunksize=64): } if item.mb_trackid: item_data['mbid'] = item.mb_trackid - log.debug(u'submitting MBID') + log.debug('submitting MBID') else: item_data.update({ 'track': item.title, @@ -260,7 +297,7 @@ def submit_items(log, userkey, items, chunksize=64): 'trackno': item.track, 'discno': item.disc, }) - log.debug(u'submitting textual metadata') + log.debug('submitting textual metadata') data.append(item_data) # If we have enough data, submit a chunk. @@ -281,28 +318,28 @@ def fingerprint_item(log, item, write=False): """ # Get a fingerprint and length for this track. if not item.length: - log.info(u'{0}: no duration available', + log.info('{0}: no duration available', util.displayable_path(item.path)) elif item.acoustid_fingerprint: if write: - log.info(u'{0}: fingerprint exists, skipping', + log.info('{0}: fingerprint exists, skipping', util.displayable_path(item.path)) else: - log.info(u'{0}: using existing fingerprint', + log.info('{0}: using existing fingerprint', util.displayable_path(item.path)) - return item.acoustid_fingerprint + return item.acoustid_fingerprint else: - log.info(u'{0}: fingerprinting', + log.info('{0}: fingerprinting', util.displayable_path(item.path)) try: _, fp = acoustid.fingerprint_file(util.syspath(item.path)) - item.acoustid_fingerprint = fp + item.acoustid_fingerprint = fp.decode() if write: - log.info(u'{0}: writing fingerprint', + log.info('{0}: writing fingerprint', util.displayable_path(item.path)) item.try_write() if item._db: item.store() return item.acoustid_fingerprint except acoustid.FingerprintGenerationError as exc: - log.info(u'fingerprint generation failed: {0}', exc) + log.info('fingerprint generation failed: {0}', exc) diff --git a/libs/common/beetsplug/convert.py b/libs/common/beetsplug/convert.py index d1223596..6bc07c28 100644 --- a/libs/common/beetsplug/convert.py +++ b/libs/common/beetsplug/convert.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Jakob Schnitzer. # @@ -15,20 +14,18 @@ """Converts tracks or albums to external directory """ -from __future__ import division, absolute_import, print_function +from beets.util import par_map, decode_commandline_path, arg_encoding import os import threading import subprocess import tempfile import shlex -import six from string import Template -import platform from beets import ui, util, plugins, config from beets.plugins import BeetsPlugin -from beets.util.confit import ConfigTypeError +from confuse import ConfigTypeError from beets import art from beets.util.artresizer import ArtResizer from beets.library import parse_query_string @@ -39,8 +36,8 @@ _temp_files = [] # Keep track of temporary transcoded files for deletion. # Some convenient alternate names for formats. ALIASES = { - u'wma': u'windows media', - u'vorbis': u'ogg', + 'wma': 'windows media', + 'vorbis': 'ogg', } LOSSLESS_FORMATS = ['ape', 'flac', 'alac', 'wav', 'aiff'] @@ -68,7 +65,7 @@ def get_format(fmt=None): extension = format_info.get('extension', fmt) except KeyError: raise ui.UserError( - u'convert: format {0} needs the "command" field' + 'convert: format {} needs the "command" field' .format(fmt) ) except ConfigTypeError: @@ -81,7 +78,7 @@ def get_format(fmt=None): command = config['convert']['command'].as_str() elif 'opts' in keys: # Undocumented option for backwards compatibility with < 1.3.1. - command = u'ffmpeg -i $source -y {0} $dest'.format( + command = 'ffmpeg -i $source -y {} $dest'.format( config['convert']['opts'].as_str() ) if 'extension' in keys: @@ -110,70 +107,81 @@ def should_transcode(item, fmt): class ConvertPlugin(BeetsPlugin): def __init__(self): - super(ConvertPlugin, self).__init__() + super().__init__() self.config.add({ - u'dest': None, - u'pretend': False, - u'threads': util.cpu_count(), - u'format': u'mp3', - u'formats': { - u'aac': { - u'command': u'ffmpeg -i $source -y -vn -acodec aac ' - u'-aq 1 $dest', - u'extension': u'm4a', + 'dest': None, + 'pretend': False, + 'link': False, + 'hardlink': False, + 'threads': util.cpu_count(), + 'format': 'mp3', + 'id3v23': 'inherit', + 'formats': { + 'aac': { + 'command': 'ffmpeg -i $source -y -vn -acodec aac ' + '-aq 1 $dest', + 'extension': 'm4a', }, - u'alac': { - u'command': u'ffmpeg -i $source -y -vn -acodec alac $dest', - u'extension': u'm4a', + 'alac': { + 'command': 'ffmpeg -i $source -y -vn -acodec alac $dest', + 'extension': 'm4a', }, - u'flac': u'ffmpeg -i $source -y -vn -acodec flac $dest', - u'mp3': u'ffmpeg -i $source -y -vn -aq 2 $dest', - u'opus': - u'ffmpeg -i $source -y -vn -acodec libopus -ab 96k $dest', - u'ogg': - u'ffmpeg -i $source -y -vn -acodec libvorbis -aq 3 $dest', - u'wma': - u'ffmpeg -i $source -y -vn -acodec wmav2 -vn $dest', + 'flac': 'ffmpeg -i $source -y -vn -acodec flac $dest', + 'mp3': 'ffmpeg -i $source -y -vn -aq 2 $dest', + 'opus': + 'ffmpeg -i $source -y -vn -acodec libopus -ab 96k $dest', + 'ogg': + 'ffmpeg -i $source -y -vn -acodec libvorbis -aq 3 $dest', + 'wma': + 'ffmpeg -i $source -y -vn -acodec wmav2 -vn $dest', }, - u'max_bitrate': 500, - u'auto': False, - u'tmpdir': None, - u'quiet': False, - u'embed': True, - u'paths': {}, - u'no_convert': u'', - u'never_convert_lossy_files': False, - u'copy_album_art': False, - u'album_art_maxwidth': 0, + 'max_bitrate': 500, + 'auto': False, + 'tmpdir': None, + 'quiet': False, + 'embed': True, + 'paths': {}, + 'no_convert': '', + 'never_convert_lossy_files': False, + 'copy_album_art': False, + 'album_art_maxwidth': 0, + 'delete_originals': False, }) self.early_import_stages = [self.auto_convert] self.register_listener('import_task_files', self._cleanup) def commands(self): - cmd = ui.Subcommand('convert', help=u'convert to external location') + cmd = ui.Subcommand('convert', help='convert to external location') cmd.parser.add_option('-p', '--pretend', action='store_true', - help=u'show actions but do nothing') + help='show actions but do nothing') cmd.parser.add_option('-t', '--threads', action='store', type='int', - help=u'change the number of threads, \ + help='change the number of threads, \ defaults to maximum available processors') cmd.parser.add_option('-k', '--keep-new', action='store_true', - dest='keep_new', help=u'keep only the converted \ + dest='keep_new', help='keep only the converted \ and move the old files') cmd.parser.add_option('-d', '--dest', action='store', - help=u'set the destination directory') + help='set the destination directory') cmd.parser.add_option('-f', '--format', action='store', dest='format', - help=u'set the target format of the tracks') + help='set the target format of the tracks') cmd.parser.add_option('-y', '--yes', action='store_true', dest='yes', - help=u'do not ask for confirmation') + help='do not ask for confirmation') + cmd.parser.add_option('-l', '--link', action='store_true', dest='link', + help='symlink files that do not \ + need transcoding.') + cmd.parser.add_option('-H', '--hardlink', action='store_true', + dest='hardlink', + help='hardlink files that do not \ + need transcoding. Overrides --link.') cmd.parser.add_album_option() cmd.func = self.convert_func return [cmd] def auto_convert(self, config, task): if self.config['auto']: - for item in task.imported_items(): - self.convert_on_import(config.lib, item) + par_map(lambda item: self.convert_on_import(config.lib, item), + task.imported_items()) # Utilities converted from functions to methods on logging overhaul @@ -191,22 +199,11 @@ class ConvertPlugin(BeetsPlugin): quiet = self.config['quiet'].get(bool) if not quiet and not pretend: - self._log.info(u'Encoding {0}', util.displayable_path(source)) + self._log.info('Encoding {0}', util.displayable_path(source)) - # On Python 3, we need to construct the command to invoke as a - # Unicode string. On Unix, this is a little unfortunate---the OS is - # expecting bytes---so we use surrogate escaping and decode with the - # argument encoding, which is the same encoding that will then be - # *reversed* to recover the same bytes before invoking the OS. On - # Windows, we want to preserve the Unicode filename "as is." - if not six.PY2: - command = command.decode(util.arg_encoding(), 'surrogateescape') - if platform.system() == 'Windows': - source = source.decode(util._fsencoding()) - dest = dest.decode(util._fsencoding()) - else: - source = source.decode(util.arg_encoding(), 'surrogateescape') - dest = dest.decode(util.arg_encoding(), 'surrogateescape') + command = command.decode(arg_encoding(), 'surrogateescape') + source = decode_commandline_path(source) + dest = decode_commandline_path(dest) # Substitute $source and $dest in the argument list. args = shlex.split(command) @@ -216,22 +213,19 @@ class ConvertPlugin(BeetsPlugin): 'source': source, 'dest': dest, }) - if six.PY2: - encode_cmd.append(args[i]) - else: - encode_cmd.append(args[i].encode(util.arg_encoding())) + encode_cmd.append(args[i].encode(util.arg_encoding())) if pretend: - self._log.info(u'{0}', u' '.join(ui.decargs(args))) + self._log.info('{0}', ' '.join(ui.decargs(args))) return try: util.command_output(encode_cmd) except subprocess.CalledProcessError as exc: # Something went wrong (probably Ctrl+C), remove temporary files - self._log.info(u'Encoding {0} failed. Cleaning up...', + self._log.info('Encoding {0} failed. Cleaning up...', util.displayable_path(source)) - self._log.debug(u'Command {0} exited with status {1}: {2}', + self._log.debug('Command {0} exited with status {1}: {2}', args, exc.returncode, exc.output) @@ -240,17 +234,17 @@ class ConvertPlugin(BeetsPlugin): raise except OSError as exc: raise ui.UserError( - u"convert: couldn't invoke '{0}': {1}".format( - u' '.join(ui.decargs(args)), exc + "convert: couldn't invoke '{}': {}".format( + ' '.join(ui.decargs(args)), exc ) ) if not quiet and not pretend: - self._log.info(u'Finished encoding {0}', + self._log.info('Finished encoding {0}', util.displayable_path(source)) def convert_item(self, dest_dir, keep_new, path_formats, fmt, - pretend=False): + pretend=False, link=False, hardlink=False): """A pipeline thread that converts `Item` objects from a library. """ @@ -283,41 +277,60 @@ class ConvertPlugin(BeetsPlugin): util.mkdirall(dest) if os.path.exists(util.syspath(dest)): - self._log.info(u'Skipping {0} (target file exists)', + self._log.info('Skipping {0} (target file exists)', util.displayable_path(item.path)) continue if keep_new: if pretend: - self._log.info(u'mv {0} {1}', + self._log.info('mv {0} {1}', util.displayable_path(item.path), util.displayable_path(original)) else: - self._log.info(u'Moving to {0}', + self._log.info('Moving to {0}', util.displayable_path(original)) util.move(item.path, original) if should_transcode(item, fmt): + linked = False try: self.encode(command, original, converted, pretend) except subprocess.CalledProcessError: continue else: + linked = link or hardlink if pretend: - self._log.info(u'cp {0} {1}', + msg = 'ln' if hardlink else ('ln -s' if link else 'cp') + + self._log.info('{2} {0} {1}', util.displayable_path(original), - util.displayable_path(converted)) + util.displayable_path(converted), + msg) else: # No transcoding necessary. - self._log.info(u'Copying {0}', - util.displayable_path(item.path)) - util.copy(original, converted) + msg = 'Hardlinking' if hardlink \ + else ('Linking' if link else 'Copying') + + self._log.info('{1} {0}', + util.displayable_path(item.path), + msg) + + if hardlink: + util.hardlink(original, converted) + elif link: + util.link(original, converted) + else: + util.copy(original, converted) if pretend: continue + id3v23 = self.config['id3v23'].as_choice([True, False, 'inherit']) + if id3v23 == 'inherit': + id3v23 = None + # Write tags from the database to the converted file. - item.try_write(path=converted) + item.try_write(path=converted, id3v23=id3v23) if keep_new: # If we're keeping the transcoded file, read it again (after @@ -326,13 +339,13 @@ class ConvertPlugin(BeetsPlugin): item.read() item.store() # Store new path and audio data. - if self.config['embed']: - album = item.get_album() + if self.config['embed'] and not linked: + album = item._cached_album if album and album.artpath: - self._log.debug(u'embedding album art from {}', + self._log.debug('embedding album art from {}', util.displayable_path(album.artpath)) art.embed_item(self._log, item, album.artpath, - itempath=converted) + itempath=converted, id3v23=id3v23) if keep_new: plugins.send('after_convert', item=item, @@ -341,7 +354,8 @@ class ConvertPlugin(BeetsPlugin): plugins.send('after_convert', item=item, dest=converted, keepnew=False) - def copy_album_art(self, album, dest_dir, path_formats, pretend=False): + def copy_album_art(self, album, dest_dir, path_formats, pretend=False, + link=False, hardlink=False): """Copies or converts the associated cover art of the album. Album must have at least one track. """ @@ -369,7 +383,7 @@ class ConvertPlugin(BeetsPlugin): util.mkdirall(dest) if os.path.exists(util.syspath(dest)): - self._log.info(u'Skipping {0} (target file exists)', + self._log.info('Skipping {0} (target file exists)', util.displayable_path(album.artpath)) return @@ -383,31 +397,43 @@ class ConvertPlugin(BeetsPlugin): if size: resize = size[0] > maxwidth else: - self._log.warning(u'Could not get size of image (please see ' - u'documentation for dependencies).') + self._log.warning('Could not get size of image (please see ' + 'documentation for dependencies).') # Either copy or resize (while copying) the image. if resize: - self._log.info(u'Resizing cover art from {0} to {1}', + self._log.info('Resizing cover art from {0} to {1}', util.displayable_path(album.artpath), util.displayable_path(dest)) if not pretend: ArtResizer.shared.resize(maxwidth, album.artpath, dest) else: if pretend: - self._log.info(u'cp {0} {1}', + msg = 'ln' if hardlink else ('ln -s' if link else 'cp') + + self._log.info('{2} {0} {1}', util.displayable_path(album.artpath), - util.displayable_path(dest)) + util.displayable_path(dest), + msg) else: - self._log.info(u'Copying cover art to {0}', + msg = 'Hardlinking' if hardlink \ + else ('Linking' if link else 'Copying') + + self._log.info('{2} cover art from {0} to {1}', util.displayable_path(album.artpath), - util.displayable_path(dest)) - util.copy(album.artpath, dest) + util.displayable_path(dest), + msg) + if hardlink: + util.hardlink(album.artpath, dest) + elif link: + util.link(album.artpath, dest) + else: + util.copy(album.artpath, dest) def convert_func(self, lib, opts, args): dest = opts.dest or self.config['dest'].get() if not dest: - raise ui.UserError(u'no convert destination set') + raise ui.UserError('no convert destination set') dest = util.bytestring_path(dest) threads = opts.threads or self.config['threads'].get(int) @@ -421,33 +447,46 @@ class ConvertPlugin(BeetsPlugin): else: pretend = self.config['pretend'].get(bool) + if opts.hardlink is not None: + hardlink = opts.hardlink + link = False + elif opts.link is not None: + hardlink = False + link = opts.link + else: + hardlink = self.config['hardlink'].get(bool) + link = self.config['link'].get(bool) + if opts.album: albums = lib.albums(ui.decargs(args)) items = [i for a in albums for i in a.items()] if not pretend: for a in albums: - ui.print_(format(a, u'')) + ui.print_(format(a, '')) else: items = list(lib.items(ui.decargs(args))) if not pretend: for i in items: - ui.print_(format(i, u'')) + ui.print_(format(i, '')) if not items: - self._log.error(u'Empty query result.') + self._log.error('Empty query result.') return - if not (pretend or opts.yes or ui.input_yn(u"Convert? (Y/n)")): + if not (pretend or opts.yes or ui.input_yn("Convert? (Y/n)")): return if opts.album and self.config['copy_album_art']: for album in albums: - self.copy_album_art(album, dest, path_formats, pretend) + self.copy_album_art(album, dest, path_formats, pretend, + link, hardlink) convert = [self.convert_item(dest, opts.keep_new, path_formats, fmt, - pretend) + pretend, + link, + hardlink) for _ in range(threads)] pipe = util.pipeline.Pipeline([iter(items), convert]) pipe.run_parallel() @@ -477,11 +516,16 @@ class ConvertPlugin(BeetsPlugin): # Change the newly-imported database entry to point to the # converted file. + source_path = item.path item.path = dest item.write() item.read() # Load new audio information data. item.store() + if self.config['delete_originals']: + self._log.info('Removing original file {0}', source_path) + util.remove(source_path, False) + def _cleanup(self, task, session): for path in task.old_paths: if path in _temp_files: diff --git a/libs/common/beetsplug/cue.py b/libs/common/beetsplug/cue.py deleted file mode 100644 index fd564b55..00000000 --- a/libs/common/beetsplug/cue.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Bruno Cauet -# Split an album-file in tracks thanks a cue file - -from __future__ import division, absolute_import, print_function - -import subprocess -from os import path -from glob import glob - -from beets.util import command_output, displayable_path -from beets.plugins import BeetsPlugin -from beets.autotag import TrackInfo - - -class CuePlugin(BeetsPlugin): - def __init__(self): - super(CuePlugin, self).__init__() - # this does not seem supported by shnsplit - self.config.add({ - 'keep_before': .1, - 'keep_after': .9, - }) - - # self.register_listener('import_task_start', self.look_for_cues) - - def candidates(self, items, artist, album, va_likely): - import pdb - pdb.set_trace() - - def item_candidates(self, item, artist, album): - dir = path.dirname(item.path) - cues = glob.glob(path.join(dir, "*.cue")) - if not cues: - return - if len(cues) > 1: - self._log.info(u"Found multiple cue files doing nothing: {0}", - list(map(displayable_path, cues))) - - cue_file = cues[0] - self._log.info("Found {} for {}", displayable_path(cue_file), item) - - try: - # careful: will ask for input in case of conflicts - command_output(['shnsplit', '-f', cue_file, item.path]) - except (subprocess.CalledProcessError, OSError): - self._log.exception(u'shnsplit execution failed') - return - - tracks = glob(path.join(dir, "*.wav")) - self._log.info("Generated {0} tracks", len(tracks)) - for t in tracks: - title = "dunno lol" - track_id = "wtf" - index = int(path.basename(t)[len("split-track"):-len(".wav")]) - yield TrackInfo(title, track_id, index=index, artist=artist) - # generate TrackInfo instances diff --git a/libs/common/beetsplug/deezer.py b/libs/common/beetsplug/deezer.py new file mode 100644 index 00000000..5f158f93 --- /dev/null +++ b/libs/common/beetsplug/deezer.py @@ -0,0 +1,230 @@ +# This file is part of beets. +# Copyright 2019, Rahul Ahuja. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Adds Deezer release and track search support to the autotagger +""" + +import collections + +import unidecode +import requests + +from beets import ui +from beets.autotag import AlbumInfo, TrackInfo +from beets.plugins import MetadataSourcePlugin, BeetsPlugin + + +class DeezerPlugin(MetadataSourcePlugin, BeetsPlugin): + data_source = 'Deezer' + + # Base URLs for the Deezer API + # Documentation: https://developers.deezer.com/api/ + search_url = 'https://api.deezer.com/search/' + album_url = 'https://api.deezer.com/album/' + track_url = 'https://api.deezer.com/track/' + + id_regex = { + 'pattern': r'(^|deezer\.com/)([a-z]*/)?({}/)?(\d+)', + 'match_group': 4, + } + + def __init__(self): + super().__init__() + + def album_for_id(self, album_id): + """Fetch an album by its Deezer ID or URL and return an + AlbumInfo object or None if the album is not found. + + :param album_id: Deezer ID or URL for the album. + :type album_id: str + :return: AlbumInfo object for album. + :rtype: beets.autotag.hooks.AlbumInfo or None + """ + deezer_id = self._get_id('album', album_id) + if deezer_id is None: + return None + + album_data = requests.get(self.album_url + deezer_id).json() + artist, artist_id = self.get_artist(album_data['contributors']) + + release_date = album_data['release_date'] + date_parts = [int(part) for part in release_date.split('-')] + num_date_parts = len(date_parts) + + if num_date_parts == 3: + year, month, day = date_parts + elif num_date_parts == 2: + year, month = date_parts + day = None + elif num_date_parts == 1: + year = date_parts[0] + month = None + day = None + else: + raise ui.UserError( + "Invalid `release_date` returned " + "by {} API: '{}'".format(self.data_source, release_date) + ) + + tracks_data = requests.get( + self.album_url + deezer_id + '/tracks' + ).json()['data'] + if not tracks_data: + return None + tracks = [] + medium_totals = collections.defaultdict(int) + for i, track_data in enumerate(tracks_data, start=1): + track = self._get_track(track_data) + track.index = i + medium_totals[track.medium] += 1 + tracks.append(track) + for track in tracks: + track.medium_total = medium_totals[track.medium] + + return AlbumInfo( + album=album_data['title'], + album_id=deezer_id, + artist=artist, + artist_credit=self.get_artist([album_data['artist']])[0], + artist_id=artist_id, + tracks=tracks, + albumtype=album_data['record_type'], + va=len(album_data['contributors']) == 1 + and artist.lower() == 'various artists', + year=year, + month=month, + day=day, + label=album_data['label'], + mediums=max(medium_totals.keys()), + data_source=self.data_source, + data_url=album_data['link'], + ) + + def _get_track(self, track_data): + """Convert a Deezer track object dict to a TrackInfo object. + + :param track_data: Deezer Track object dict + :type track_data: dict + :return: TrackInfo object for track + :rtype: beets.autotag.hooks.TrackInfo + """ + artist, artist_id = self.get_artist( + track_data.get('contributors', [track_data['artist']]) + ) + return TrackInfo( + title=track_data['title'], + track_id=track_data['id'], + artist=artist, + artist_id=artist_id, + length=track_data['duration'], + index=track_data['track_position'], + medium=track_data['disk_number'], + medium_index=track_data['track_position'], + data_source=self.data_source, + data_url=track_data['link'], + ) + + def track_for_id(self, track_id=None, track_data=None): + """Fetch a track by its Deezer ID or URL and return a + TrackInfo object or None if the track is not found. + + :param track_id: (Optional) Deezer ID or URL for the track. Either + ``track_id`` or ``track_data`` must be provided. + :type track_id: str + :param track_data: (Optional) Simplified track object dict. May be + provided instead of ``track_id`` to avoid unnecessary API calls. + :type track_data: dict + :return: TrackInfo object for track + :rtype: beets.autotag.hooks.TrackInfo or None + """ + if track_data is None: + deezer_id = self._get_id('track', track_id) + if deezer_id is None: + return None + track_data = requests.get(self.track_url + deezer_id).json() + track = self._get_track(track_data) + + # Get album's tracks to set `track.index` (position on the entire + # release) and `track.medium_total` (total number of tracks on + # the track's disc). + album_tracks_data = requests.get( + self.album_url + str(track_data['album']['id']) + '/tracks' + ).json()['data'] + medium_total = 0 + for i, track_data in enumerate(album_tracks_data, start=1): + if track_data['disk_number'] == track.medium: + medium_total += 1 + if track_data['id'] == track.track_id: + track.index = i + track.medium_total = medium_total + return track + + @staticmethod + def _construct_search_query(filters=None, keywords=''): + """Construct a query string with the specified filters and keywords to + be provided to the Deezer Search API + (https://developers.deezer.com/api/search). + + :param filters: (Optional) Field filters to apply. + :type filters: dict + :param keywords: (Optional) Query keywords to use. + :type keywords: str + :return: Query string to be provided to the Search API. + :rtype: str + """ + query_components = [ + keywords, + ' '.join(f'{k}:"{v}"' for k, v in filters.items()), + ] + query = ' '.join([q for q in query_components if q]) + if not isinstance(query, str): + query = query.decode('utf8') + return unidecode.unidecode(query) + + def _search_api(self, query_type, filters=None, keywords=''): + """Query the Deezer Search API for the specified ``keywords``, applying + the provided ``filters``. + + :param query_type: The Deezer Search API method to use. Valid types + are: 'album', 'artist', 'history', 'playlist', 'podcast', + 'radio', 'track', 'user', and 'track'. + :type query_type: str + :param filters: (Optional) Field filters to apply. + :type filters: dict + :param keywords: (Optional) Query keywords to use. + :type keywords: str + :return: JSON data for the class:`Response ` object or None + if no search results are returned. + :rtype: dict or None + """ + query = self._construct_search_query( + keywords=keywords, filters=filters + ) + if not query: + return None + self._log.debug( + f"Searching {self.data_source} for '{query}'" + ) + response = requests.get( + self.search_url + query_type, params={'q': query} + ) + response.raise_for_status() + response_data = response.json().get('data', []) + self._log.debug( + "Found {} result(s) from {} for '{}'", + len(response_data), + self.data_source, + query, + ) + return response_data diff --git a/libs/common/beetsplug/discogs.py b/libs/common/beetsplug/discogs.py index eeb87d31..d015e420 100644 --- a/libs/common/beetsplug/discogs.py +++ b/libs/common/beetsplug/discogs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -14,19 +13,18 @@ # included in all copies or substantial portions of the Software. """Adds Discogs album search support to the autotagger. Requires the -discogs-client library. +python3-discogs-client library. """ -from __future__ import division, absolute_import, print_function import beets.ui from beets import config -from beets.autotag.hooks import AlbumInfo, TrackInfo, Distance -from beets.plugins import BeetsPlugin -from beets.util import confit +from beets.autotag.hooks import AlbumInfo, TrackInfo +from beets.plugins import MetadataSourcePlugin, BeetsPlugin, get_distance +import confuse from discogs_client import Release, Master, Client from discogs_client.exceptions import DiscogsAPIError from requests.exceptions import ConnectionError -from six.moves import http_client +import http.client import beets import re import time @@ -37,10 +35,12 @@ import traceback from string import ascii_lowercase -USER_AGENT = u'beets/{0} +http://beets.io/'.format(beets.__version__) +USER_AGENT = f'beets/{beets.__version__} +https://beets.io/' +API_KEY = 'rAzVUQYRaoFjeBjyWuWZ' +API_SECRET = 'plxtUTqoCzwxZpqdPysCwGuBSmZNdZVy' # Exceptions that discogs_client should really handle but does not. -CONNECTION_ERRORS = (ConnectionError, socket.error, http_client.HTTPException, +CONNECTION_ERRORS = (ConnectionError, socket.error, http.client.HTTPException, ValueError, # JSON decoding raises a ValueError. DiscogsAPIError) @@ -48,13 +48,15 @@ CONNECTION_ERRORS = (ConnectionError, socket.error, http_client.HTTPException, class DiscogsPlugin(BeetsPlugin): def __init__(self): - super(DiscogsPlugin, self).__init__() + super().__init__() self.config.add({ - 'apikey': 'rAzVUQYRaoFjeBjyWuWZ', - 'apisecret': 'plxtUTqoCzwxZpqdPysCwGuBSmZNdZVy', + 'apikey': API_KEY, + 'apisecret': API_SECRET, 'tokenfile': 'discogs_token.json', 'source_weight': 0.5, 'user_token': '', + 'separator': ', ', + 'index_tracks': False, }) self.config['apikey'].redact = True self.config['apisecret'].redact = True @@ -71,6 +73,8 @@ class DiscogsPlugin(BeetsPlugin): # Try using a configured user token (bypassing OAuth login). user_token = self.config['user_token'].as_str() if user_token: + # The rate limit for authenticated users goes up to 60 + # requests per minute. self.discogs_client = Client(USER_AGENT, user_token=user_token) return @@ -78,7 +82,7 @@ class DiscogsPlugin(BeetsPlugin): try: with open(self._tokenfile()) as f: tokendata = json.load(f) - except IOError: + except OSError: # No token yet. Generate one. token, secret = self.authenticate(c_key, c_secret) else: @@ -97,7 +101,7 @@ class DiscogsPlugin(BeetsPlugin): def _tokenfile(self): """Get the path to the JSON file for storing the OAuth token. """ - return self.config['tokenfile'].get(confit.Filename(in_app_dir=True)) + return self.config['tokenfile'].get(confuse.Filename(in_app_dir=True)) def authenticate(self, c_key, c_secret): # Get the link for the OAuth page. @@ -105,24 +109,24 @@ class DiscogsPlugin(BeetsPlugin): try: _, _, url = auth_client.get_authorize_url() except CONNECTION_ERRORS as e: - self._log.debug(u'connection error: {0}', e) - raise beets.ui.UserError(u'communication with Discogs failed') + self._log.debug('connection error: {0}', e) + raise beets.ui.UserError('communication with Discogs failed') - beets.ui.print_(u"To authenticate with Discogs, visit:") + beets.ui.print_("To authenticate with Discogs, visit:") beets.ui.print_(url) # Ask for the code and validate it. - code = beets.ui.input_(u"Enter the code:") + code = beets.ui.input_("Enter the code:") try: token, secret = auth_client.get_access_token(code) except DiscogsAPIError: - raise beets.ui.UserError(u'Discogs authorization failed') + raise beets.ui.UserError('Discogs authorization failed') except CONNECTION_ERRORS as e: - self._log.debug(u'connection error: {0}', e) - raise beets.ui.UserError(u'Discogs token request failed') + self._log.debug('connection error: {0}', e) + raise beets.ui.UserError('Discogs token request failed') # Save the token for later use. - self._log.debug(u'Discogs token {0}, secret {1}', token, secret) + self._log.debug('Discogs token {0}, secret {1}', token, secret) with open(self._tokenfile(), 'w') as f: json.dump({'token': token, 'secret': secret}, f) @@ -131,12 +135,22 @@ class DiscogsPlugin(BeetsPlugin): def album_distance(self, items, album_info, mapping): """Returns the album distance. """ - dist = Distance() - if album_info.data_source == 'Discogs': - dist.add('source', self.config['source_weight'].as_number()) - return dist + return get_distance( + data_source='Discogs', + info=album_info, + config=self.config + ) - def candidates(self, items, artist, album, va_likely): + def track_distance(self, item, track_info): + """Returns the track distance. + """ + return get_distance( + data_source='Discogs', + info=track_info, + config=self.config + ) + + def candidates(self, items, artist, album, va_likely, extra_tags=None): """Returns a list of AlbumInfo objects for discogs search results matching an album and artist (if not various). """ @@ -146,20 +160,45 @@ class DiscogsPlugin(BeetsPlugin): if va_likely: query = album else: - query = '%s %s' % (artist, album) + query = f'{artist} {album}' try: return self.get_albums(query) except DiscogsAPIError as e: - self._log.debug(u'API Error: {0} (query: {1})', e, query) + self._log.debug('API Error: {0} (query: {1})', e, query) if e.status_code == 401: self.reset_auth() return self.candidates(items, artist, album, va_likely) else: return [] except CONNECTION_ERRORS: - self._log.debug(u'Connection error in album search', exc_info=True) + self._log.debug('Connection error in album search', exc_info=True) return [] + @staticmethod + def extract_release_id_regex(album_id): + """Returns the Discogs_id or None.""" + # Discogs-IDs are simple integers. In order to avoid confusion with + # other metadata plugins, we only look for very specific formats of the + # input string: + # - plain integer, optionally wrapped in brackets and prefixed by an + # 'r', as this is how discogs displays the release ID on its webpage. + # - legacy url format: discogs.com//release/ + # - current url format: discogs.com/release/- + # See #291, #4080 and #4085 for the discussions leading up to these + # patterns. + # Regex has been tested here https://regex101.com/r/wyLdB4/2 + + for pattern in [ + r'^\[?r?(?P\d+)\]?$', + r'discogs\.com/release/(?P\d+)-', + r'discogs\.com/[^/]+/release/(?P\d+)', + ]: + match = re.search(pattern, album_id) + if match: + return int(match.group('id')) + + return None + def album_for_id(self, album_id): """Fetches an album by its Discogs ID and returns an AlbumInfo object or None if the album is not found. @@ -167,28 +206,28 @@ class DiscogsPlugin(BeetsPlugin): if not self.discogs_client: return - self._log.debug(u'Searching for release {0}', album_id) - # Discogs-IDs are simple integers. We only look for those at the end - # of an input string as to avoid confusion with other metadata plugins. - # An optional bracket can follow the integer, as this is how discogs - # displays the release ID on its webpage. - match = re.search(r'(^|\[*r|discogs\.com/.+/release/)(\d+)($|\])', - album_id) - if not match: + self._log.debug('Searching for release {0}', album_id) + + discogs_id = self.extract_release_id_regex(album_id) + + if not discogs_id: return None - result = Release(self.discogs_client, {'id': int(match.group(2))}) + + result = Release(self.discogs_client, {'id': discogs_id}) # Try to obtain title to verify that we indeed have a valid Release try: getattr(result, 'title') except DiscogsAPIError as e: if e.status_code != 404: - self._log.debug(u'API Error: {0} (query: {1})', e, result._uri) + self._log.debug('API Error: {0} (query: {1})', e, + result.data['resource_url']) if e.status_code == 401: self.reset_auth() return self.album_for_id(album_id) return None except CONNECTION_ERRORS: - self._log.debug(u'Connection error in album lookup', exc_info=True) + self._log.debug('Connection error in album lookup', + exc_info=True) return None return self.get_album_info(result) @@ -199,18 +238,17 @@ class DiscogsPlugin(BeetsPlugin): # cause a query to return no results, even if they match the artist or # album title. Use `re.UNICODE` flag to avoid stripping non-english # word characters. - # FIXME: Encode as ASCII to work around a bug: - # https://github.com/beetbox/beets/issues/1051 - # When the library is fixed, we should encode as UTF-8. - query = re.sub(r'(?u)\W+', ' ', query).encode('ascii', "replace") + query = re.sub(r'(?u)\W+', ' ', query) # Strip medium information from query, Things like "CD1" and "disk 1" # can also negate an otherwise positive result. - query = re.sub(br'(?i)\b(CD|disc)\s*\d+', b'', query) + query = re.sub(r'(?i)\b(CD|disc)\s*\d+', '', query) + try: releases = self.discogs_client.search(query, type='release').page(1) + except CONNECTION_ERRORS: - self._log.debug(u"Communication error while searching for {0!r}", + self._log.debug("Communication error while searching for {0!r}", query, exc_info=True) return [] return [album for album in map(self.get_album_info, releases[:5]) @@ -220,20 +258,22 @@ class DiscogsPlugin(BeetsPlugin): """Fetches a master release given its Discogs ID and returns its year or None if the master release is not found. """ - self._log.debug(u'Searching for master release {0}', master_id) + self._log.debug('Searching for master release {0}', master_id) result = Master(self.discogs_client, {'id': master_id}) + try: year = result.fetch('year') return year except DiscogsAPIError as e: if e.status_code != 404: - self._log.debug(u'API Error: {0} (query: {1})', e, result._uri) + self._log.debug('API Error: {0} (query: {1})', e, + result.data['resource_url']) if e.status_code == 401: self.reset_auth() return self.get_master_year(master_id) return None except CONNECTION_ERRORS: - self._log.debug(u'Connection error in master release lookup', + self._log.debug('Connection error in master release lookup', exc_info=True) return None @@ -252,10 +292,12 @@ class DiscogsPlugin(BeetsPlugin): # https://www.discogs.com/help/doc/submission-guidelines-general-rules if not all([result.data.get(k) for k in ['artists', 'title', 'id', 'tracklist']]): - self._log.warn(u"Release does not contain the required fields") + self._log.warning("Release does not contain the required fields") return None - artist, artist_id = self.get_artist([a.data for a in result.artists]) + artist, artist_id = MetadataSourcePlugin.get_artist( + [a.data for a in result.artists] + ) album = re.sub(r' +', ' ', result.title) album_id = result.data['id'] # Use `.data` to access the tracklist directly instead of the @@ -270,10 +312,13 @@ class DiscogsPlugin(BeetsPlugin): mediums = [t.medium for t in tracks] country = result.data.get('country') data_url = result.data.get('uri') + style = self.format(result.data.get('styles')) + genre = self.format(result.data.get('genres')) + discogs_albumid = self.extract_release_id(result.data.get('uri')) # Extract information for the optional AlbumInfo fields that are # contained on nested discogs fields. - albumtype = media = label = catalogno = None + albumtype = media = label = catalogno = labelid = None if result.data.get('formats'): albumtype = ', '.join( result.data['formats'][0].get('descriptions', [])) or None @@ -281,12 +326,13 @@ class DiscogsPlugin(BeetsPlugin): if result.data.get('labels'): label = result.data['labels'][0].get('name') catalogno = result.data['labels'][0].get('catno') + labelid = result.data['labels'][0].get('id') # Additional cleanups (various artists name, catalog number, media). if va: artist = config['va_name'].as_str() if catalogno == 'none': - catalogno = None + catalogno = None # Explicitly set the `media` for the tracks, since it is expected by # `autotag.apply_metadata`, and set `medium_total`. for track in tracks: @@ -302,36 +348,29 @@ class DiscogsPlugin(BeetsPlugin): # a master release, otherwise fetch the master release. original_year = self.get_master_year(master_id) if master_id else year - return AlbumInfo(album, album_id, artist, artist_id, tracks, asin=None, - albumtype=albumtype, va=va, year=year, month=None, - day=None, label=label, mediums=len(set(mediums)), - artist_sort=None, releasegroup_id=master_id, - catalognum=catalogno, script=None, language=None, - country=country, albumstatus=None, media=media, - albumdisambig=None, artist_credit=None, - original_year=original_year, original_month=None, - original_day=None, data_source='Discogs', - data_url=data_url) + return AlbumInfo(album=album, album_id=album_id, artist=artist, + artist_id=artist_id, tracks=tracks, + albumtype=albumtype, va=va, year=year, + label=label, mediums=len(set(mediums)), + releasegroup_id=master_id, catalognum=catalogno, + country=country, style=style, genre=genre, + media=media, original_year=original_year, + data_source='Discogs', data_url=data_url, + discogs_albumid=discogs_albumid, + discogs_labelid=labelid, discogs_artistid=artist_id) - def get_artist(self, artists): - """Returns an artist string (all artists) and an artist_id (the main - artist) for a list of discogs album or track artists. - """ - artist_id = None - bits = [] - for i, artist in enumerate(artists): - if not artist_id: - artist_id = artist['id'] - name = artist['name'] - # Strip disambiguation number. - name = re.sub(r' \(\d+\)$', '', name) - # Move articles to the front. - name = re.sub(r'(?i)^(.*?), (a|an|the)$', r'\2 \1', name) - bits.append(name) - if artist['join'] and i < len(artists) - 1: - bits.append(artist['join']) - artist = ' '.join(bits).replace(' ,', ',') or None - return artist, artist_id + def format(self, classification): + if classification: + return self.config['separator'].as_str() \ + .join(sorted(classification)) + else: + return None + + def extract_release_id(self, uri): + if uri: + return uri.split("/")[-1] + else: + return None def get_tracks(self, tracklist): """Returns a list of TrackInfo objects for a discogs tracklist. @@ -342,20 +381,34 @@ class DiscogsPlugin(BeetsPlugin): # FIXME: this is an extra precaution for making sure there are no # side effects after #2222. It should be removed after further # testing. - self._log.debug(u'{}', traceback.format_exc()) - self._log.error(u'uncaught exception in coalesce_tracks: {}', exc) + self._log.debug('{}', traceback.format_exc()) + self._log.error('uncaught exception in coalesce_tracks: {}', exc) clean_tracklist = tracklist tracks = [] index_tracks = {} index = 0 + # Distinct works and intra-work divisions, as defined by index tracks. + divisions, next_divisions = [], [] for track in clean_tracklist: # Only real tracks have `position`. Otherwise, it's an index track. if track['position']: index += 1 - track_info = self.get_track_info(track, index) + if next_divisions: + # End of a block of index tracks: update the current + # divisions. + divisions += next_divisions + del next_divisions[:] + track_info = self.get_track_info(track, index, divisions) track_info.track_alt = track['position'] tracks.append(track_info) else: + next_divisions.append(track['title']) + # We expect new levels of division at the beginning of the + # tracklist (and possibly elsewhere). + try: + divisions.pop() + except IndexError: + pass index_tracks[index + 1] = track['title'] # Fix up medium and medium_index for each track. Discogs position is @@ -367,7 +420,7 @@ class DiscogsPlugin(BeetsPlugin): # If a medium has two sides (ie. vinyl or cassette), each pair of # consecutive sides should belong to the same medium. if all([track.medium is not None for track in tracks]): - m = sorted(set([track.medium.lower() for track in tracks])) + m = sorted({track.medium.lower() for track in tracks}) # If all track.medium are single consecutive letters, assume it is # a 2-sided medium. if ''.join(m) in ascii_lowercase: @@ -426,7 +479,7 @@ class DiscogsPlugin(BeetsPlugin): # Calculate position based on first subtrack, without subindex. idx, medium_idx, sub_idx = \ self.get_track_index(subtracks[0]['position']) - position = '%s%s' % (idx or '', medium_idx or '') + position = '{}{}'.format(idx or '', medium_idx or '') if tracklist and not tracklist[-1]['position']: # Assume the previous index track contains the track title. @@ -444,6 +497,12 @@ class DiscogsPlugin(BeetsPlugin): for subtrack in subtracks: if not subtrack.get('artists'): subtrack['artists'] = index_track['artists'] + # Concatenate index with track title when index_tracks + # option is set + if self.config['index_tracks']: + for subtrack in subtracks: + subtrack['title'] = '{}: {}'.format( + index_track['title'], subtrack['title']) tracklist.extend(subtracks) else: # Merge the subtracks, pick a title, and append the new track. @@ -490,18 +549,23 @@ class DiscogsPlugin(BeetsPlugin): return tracklist - def get_track_info(self, track, index): + def get_track_info(self, track, index, divisions): """Returns a TrackInfo object for a discogs track. """ title = track['title'] + if self.config['index_tracks']: + prefix = ', '.join(divisions) + if prefix: + title = f'{prefix}: {title}' track_id = None medium, medium_index, _ = self.get_track_index(track['position']) - artist, artist_id = self.get_artist(track.get('artists', [])) + artist, artist_id = MetadataSourcePlugin.get_artist( + track.get('artists', []) + ) length = self.get_track_length(track['duration']) - return TrackInfo(title, track_id, artist=artist, artist_id=artist_id, - length=length, index=index, - medium=medium, medium_index=medium_index, - artist_sort=None, disctitle=None, artist_credit=None) + return TrackInfo(title=title, track_id=track_id, artist=artist, + artist_id=artist_id, length=length, index=index, + medium=medium, medium_index=medium_index) def get_track_index(self, position): """Returns the medium, medium index and subtrack index for a discogs @@ -528,7 +592,7 @@ class DiscogsPlugin(BeetsPlugin): if subindex and subindex.startswith('.'): subindex = subindex[1:] else: - self._log.debug(u'Invalid position: {0}', position) + self._log.debug('Invalid position: {0}', position) medium = index = subindex = None return medium or None, index or None, subindex or None diff --git a/libs/common/beetsplug/duplicates.py b/libs/common/beetsplug/duplicates.py index b316cfda..fdd5c175 100644 --- a/libs/common/beetsplug/duplicates.py +++ b/libs/common/beetsplug/duplicates.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Pedro Silva. # @@ -15,16 +14,15 @@ """List duplicate tracks or albums. """ -from __future__ import division, absolute_import, print_function import shlex from beets.plugins import BeetsPlugin from beets.ui import decargs, print_, Subcommand, UserError from beets.util import command_output, displayable_path, subprocess, \ - bytestring_path, MoveOperation + bytestring_path, MoveOperation, decode_commandline_path from beets.library import Item, Album -import six + PLUGIN = 'duplicates' @@ -33,7 +31,7 @@ class DuplicatesPlugin(BeetsPlugin): """List duplicate tracks or albums """ def __init__(self): - super(DuplicatesPlugin, self).__init__() + super().__init__() self.config.add({ 'album': False, @@ -56,54 +54,54 @@ class DuplicatesPlugin(BeetsPlugin): help=__doc__, aliases=['dup']) self._command.parser.add_option( - u'-c', u'--count', dest='count', + '-c', '--count', dest='count', action='store_true', - help=u'show duplicate counts', + help='show duplicate counts', ) self._command.parser.add_option( - u'-C', u'--checksum', dest='checksum', + '-C', '--checksum', dest='checksum', action='store', metavar='PROG', - help=u'report duplicates based on arbitrary command', + help='report duplicates based on arbitrary command', ) self._command.parser.add_option( - u'-d', u'--delete', dest='delete', + '-d', '--delete', dest='delete', action='store_true', - help=u'delete items from library and disk', + help='delete items from library and disk', ) self._command.parser.add_option( - u'-F', u'--full', dest='full', + '-F', '--full', dest='full', action='store_true', - help=u'show all versions of duplicate tracks or albums', + help='show all versions of duplicate tracks or albums', ) self._command.parser.add_option( - u'-s', u'--strict', dest='strict', + '-s', '--strict', dest='strict', action='store_true', - help=u'report duplicates only if all attributes are set', + help='report duplicates only if all attributes are set', ) self._command.parser.add_option( - u'-k', u'--key', dest='keys', + '-k', '--key', dest='keys', action='append', metavar='KEY', - help=u'report duplicates based on keys (use multiple times)', + help='report duplicates based on keys (use multiple times)', ) self._command.parser.add_option( - u'-M', u'--merge', dest='merge', + '-M', '--merge', dest='merge', action='store_true', - help=u'merge duplicate items', + help='merge duplicate items', ) self._command.parser.add_option( - u'-m', u'--move', dest='move', + '-m', '--move', dest='move', action='store', metavar='DEST', - help=u'move items to dest', + help='move items to dest', ) self._command.parser.add_option( - u'-o', u'--copy', dest='copy', + '-o', '--copy', dest='copy', action='store', metavar='DEST', - help=u'copy items to dest', + help='copy items to dest', ) self._command.parser.add_option( - u'-t', u'--tag', dest='tag', + '-t', '--tag', dest='tag', action='store', - help=u'tag matched items with \'k=v\' attribute', + help='tag matched items with \'k=v\' attribute', ) self._command.parser.add_all_common_options() @@ -135,16 +133,21 @@ class DuplicatesPlugin(BeetsPlugin): keys = ['mb_trackid', 'mb_albumid'] items = lib.items(decargs(args)) + # If there's nothing to do, return early. The code below assumes + # `items` to be non-empty. + if not items: + return + if path: - fmt = u'$path' + fmt = '$path' # Default format string for count mode. if count and not fmt: if album: - fmt = u'$albumartist - $album' + fmt = '$albumartist - $album' else: - fmt = u'$albumartist - $album - $title' - fmt += u': {0}' + fmt = '$albumartist - $album - $title' + fmt += ': {0}' if checksum: for i in items: @@ -170,7 +173,7 @@ class DuplicatesPlugin(BeetsPlugin): return [self._command] def _process_item(self, item, copy=False, move=False, delete=False, - tag=False, fmt=u''): + tag=False, fmt=''): """Process Item `item`. """ print_(format(item, fmt)) @@ -187,7 +190,7 @@ class DuplicatesPlugin(BeetsPlugin): k, v = tag.split('=') except Exception: raise UserError( - u"{}: can't parse k=v tag: {}".format(PLUGIN, tag) + f"{PLUGIN}: can't parse k=v tag: {tag}" ) setattr(item, k, v) item.store() @@ -197,25 +200,26 @@ class DuplicatesPlugin(BeetsPlugin): output as flexattr on a key that is the name of the program, and return the key, checksum tuple. """ - args = [p.format(file=item.path) for p in shlex.split(prog)] + args = [p.format(file=decode_commandline_path(item.path)) + for p in shlex.split(prog)] key = args[0] checksum = getattr(item, key, False) if not checksum: - self._log.debug(u'key {0} on item {1} not cached:' - u'computing checksum', + self._log.debug('key {0} on item {1} not cached:' + 'computing checksum', key, displayable_path(item.path)) try: - checksum = command_output(args) + checksum = command_output(args).stdout setattr(item, key, checksum) item.store() - self._log.debug(u'computed checksum for {0} using {1}', + self._log.debug('computed checksum for {0} using {1}', item.title, key) except subprocess.CalledProcessError as e: - self._log.debug(u'failed to checksum {0}: {1}', + self._log.debug('failed to checksum {0}: {1}', displayable_path(item.path), e) else: - self._log.debug(u'key {0} on item {1} cached:' - u'not computing checksum', + self._log.debug('key {0} on item {1} cached:' + 'not computing checksum', key, displayable_path(item.path)) return key, checksum @@ -231,12 +235,12 @@ class DuplicatesPlugin(BeetsPlugin): values = [getattr(obj, k, None) for k in keys] values = [v for v in values if v not in (None, '')] if strict and len(values) < len(keys): - self._log.debug(u'some keys {0} on item {1} are null or empty:' - u' skipping', + self._log.debug('some keys {0} on item {1} are null or empty:' + ' skipping', keys, displayable_path(obj.path)) elif (not strict and not len(values)): - self._log.debug(u'all keys {0} on item {1} are null or empty:' - u' skipping', + self._log.debug('all keys {0} on item {1} are null or empty:' + ' skipping', keys, displayable_path(obj.path)) else: key = tuple(values) @@ -264,7 +268,7 @@ class DuplicatesPlugin(BeetsPlugin): # between a bytes object and the empty Unicode # string ''. return v is not None and \ - (v != '' if isinstance(v, six.text_type) else True) + (v != '' if isinstance(v, str) else True) fields = Item.all_keys() key = lambda x: sum(1 for f in fields if truthy(getattr(x, f))) else: @@ -284,8 +288,8 @@ class DuplicatesPlugin(BeetsPlugin): if getattr(objs[0], f, None) in (None, ''): value = getattr(o, f, None) if value: - self._log.debug(u'key {0} on item {1} is null ' - u'or empty: setting from item {2}', + self._log.debug('key {0} on item {1} is null ' + 'or empty: setting from item {2}', f, displayable_path(objs[0].path), displayable_path(o.path)) setattr(objs[0], f, value) @@ -305,8 +309,8 @@ class DuplicatesPlugin(BeetsPlugin): missing = Item.from_path(i.path) missing.album_id = objs[0].id missing.add(i._db) - self._log.debug(u'item {0} missing from album {1}:' - u' merging from {2} into {3}', + self._log.debug('item {0} missing from album {1}:' + ' merging from {2} into {3}', missing, objs[0], displayable_path(o.path), diff --git a/libs/common/beetsplug/edit.py b/libs/common/beetsplug/edit.py index 631a1b58..6f03fa4d 100644 --- a/libs/common/beetsplug/edit.py +++ b/libs/common/beetsplug/edit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016 # @@ -15,7 +14,6 @@ """Open metadata information in a text editor to let the user edit it. """ -from __future__ import division, absolute_import, print_function from beets import plugins from beets import util @@ -28,7 +26,7 @@ import subprocess import yaml from tempfile import NamedTemporaryFile import os -import six +import shlex # These "safe" types can avoid the format/parse cycle that most fields go @@ -45,13 +43,13 @@ class ParseError(Exception): def edit(filename, log): """Open `filename` in a text editor. """ - cmd = util.shlex_split(util.editor_command()) + cmd = shlex.split(util.editor_command()) cmd.append(filename) - log.debug(u'invoking editor command: {!r}', cmd) + log.debug('invoking editor command: {!r}', cmd) try: subprocess.call(cmd) except OSError as exc: - raise ui.UserError(u'could not run editor command {!r}: {}'.format( + raise ui.UserError('could not run editor command {!r}: {}'.format( cmd[0], exc )) @@ -74,20 +72,20 @@ def load(s): """ try: out = [] - for d in yaml.load_all(s): + for d in yaml.safe_load_all(s): if not isinstance(d, dict): raise ParseError( - u'each entry must be a dictionary; found {}'.format( + 'each entry must be a dictionary; found {}'.format( type(d).__name__ ) ) # Convert all keys to strings. They started out as strings, # but the user may have inadvertently messed this up. - out.append({six.text_type(k): v for k, v in d.items()}) + out.append({str(k): v for k, v in d.items()}) except yaml.YAMLError as e: - raise ParseError(u'invalid YAML: {}'.format(e)) + raise ParseError(f'invalid YAML: {e}') return out @@ -143,13 +141,13 @@ def apply_(obj, data): else: # Either the field was stringified originally or the user changed # it from a safe type to an unsafe one. Parse it as a string. - obj.set_parse(key, six.text_type(value)) + obj.set_parse(key, str(value)) class EditPlugin(plugins.BeetsPlugin): def __init__(self): - super(EditPlugin, self).__init__() + super().__init__() self.config.add({ # The default fields to edit. @@ -166,18 +164,18 @@ class EditPlugin(plugins.BeetsPlugin): def commands(self): edit_command = ui.Subcommand( 'edit', - help=u'interactively edit metadata' + help='interactively edit metadata' ) edit_command.parser.add_option( - u'-f', u'--field', + '-f', '--field', metavar='FIELD', action='append', - help=u'edit this field also', + help='edit this field also', ) edit_command.parser.add_option( - u'--all', + '--all', action='store_true', dest='all', - help=u'edit all fields', + help='edit all fields', ) edit_command.parser.add_album_option() edit_command.func = self._edit_command @@ -191,7 +189,7 @@ class EditPlugin(plugins.BeetsPlugin): items, albums = _do_query(lib, query, opts.album, False) objs = albums if opts.album else items if not objs: - ui.print_(u'Nothing to edit.') + ui.print_('Nothing to edit.') return # Get the fields to edit. @@ -244,15 +242,10 @@ class EditPlugin(plugins.BeetsPlugin): old_data = [flatten(o, fields) for o in objs] # Set up a temporary file with the initial data for editing. - if six.PY2: - new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) - else: - new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False, - encoding='utf-8') + new = NamedTemporaryFile(mode='w', suffix='.yaml', delete=False, + encoding='utf-8') old_str = dump(old_data) new.write(old_str) - if six.PY2: - old_str = old_str.decode('utf-8') new.close() # Loop until we have parseable data and the user confirms. @@ -266,15 +259,15 @@ class EditPlugin(plugins.BeetsPlugin): with codecs.open(new.name, encoding='utf-8') as f: new_str = f.read() if new_str == old_str: - ui.print_(u"No changes; aborting.") + ui.print_("No changes; aborting.") return False # Parse the updated data. try: new_data = load(new_str) except ParseError as e: - ui.print_(u"Could not read data: {}".format(e)) - if ui.input_yn(u"Edit again to fix? (Y/n)", True): + ui.print_(f"Could not read data: {e}") + if ui.input_yn("Edit again to fix? (Y/n)", True): continue else: return False @@ -289,18 +282,18 @@ class EditPlugin(plugins.BeetsPlugin): for obj, obj_old in zip(objs, objs_old): changed |= ui.show_model_changes(obj, obj_old) if not changed: - ui.print_(u'No changes to apply.') + ui.print_('No changes to apply.') return False # Confirm the changes. choice = ui.input_options( - (u'continue Editing', u'apply', u'cancel') + ('continue Editing', 'apply', 'cancel') ) - if choice == u'a': # Apply. + if choice == 'a': # Apply. return True - elif choice == u'c': # Cancel. + elif choice == 'c': # Cancel. return False - elif choice == u'e': # Keep editing. + elif choice == 'e': # Keep editing. # Reset the temporary changes to the objects. I we have a # copy from above, use that, else reload from the database. objs = [(old_obj or obj) @@ -322,7 +315,7 @@ class EditPlugin(plugins.BeetsPlugin): are temporary. """ if len(old_data) != len(new_data): - self._log.warning(u'number of objects changed from {} to {}', + self._log.warning('number of objects changed from {} to {}', len(old_data), len(new_data)) obj_by_id = {o.id: o for o in objs} @@ -333,7 +326,7 @@ class EditPlugin(plugins.BeetsPlugin): forbidden = False for key in ignore_fields: if old_dict.get(key) != new_dict.get(key): - self._log.warning(u'ignoring object whose {} changed', key) + self._log.warning('ignoring object whose {} changed', key) forbidden = True break if forbidden: @@ -348,7 +341,7 @@ class EditPlugin(plugins.BeetsPlugin): # Save to the database and possibly write tags. for ob in objs: if ob._dirty: - self._log.debug(u'saving changes to {}', ob) + self._log.debug('saving changes to {}', ob) ob.try_sync(ui.should_write(), ui.should_move()) # Methods for interactive importer execution. diff --git a/libs/common/beetsplug/embedart.py b/libs/common/beetsplug/embedart.py index afe8f86f..6db46f8c 100644 --- a/libs/common/beetsplug/embedart.py +++ b/libs/common/beetsplug/embedart.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -14,7 +13,6 @@ # included in all copies or substantial portions of the Software. """Allows beets to embed album art into file metadata.""" -from __future__ import division, absolute_import, print_function import os.path @@ -34,11 +32,11 @@ def _confirm(objs, album): `album` is a Boolean indicating whether these are albums (as opposed to items). """ - noun = u'album' if album else u'file' - prompt = u'Modify artwork for {} {}{} (Y/n)?'.format( + noun = 'album' if album else 'file' + prompt = 'Modify artwork for {} {}{} (Y/n)?'.format( len(objs), noun, - u's' if len(objs) > 1 else u'' + 's' if len(objs) > 1 else '' ) # Show all the items or albums. @@ -53,39 +51,41 @@ class EmbedCoverArtPlugin(BeetsPlugin): """Allows albumart to be embedded into the actual files. """ def __init__(self): - super(EmbedCoverArtPlugin, self).__init__() + super().__init__() self.config.add({ 'maxwidth': 0, 'auto': True, 'compare_threshold': 0, 'ifempty': False, - 'remove_art_file': False + 'remove_art_file': False, + 'quality': 0, }) if self.config['maxwidth'].get(int) and not ArtResizer.shared.local: self.config['maxwidth'] = 0 - self._log.warning(u"ImageMagick or PIL not found; " - u"'maxwidth' option ignored") + self._log.warning("ImageMagick or PIL not found; " + "'maxwidth' option ignored") if self.config['compare_threshold'].get(int) and not \ ArtResizer.shared.can_compare: self.config['compare_threshold'] = 0 - self._log.warning(u"ImageMagick 6.8.7 or higher not installed; " - u"'compare_threshold' option ignored") + self._log.warning("ImageMagick 6.8.7 or higher not installed; " + "'compare_threshold' option ignored") self.register_listener('art_set', self.process_album) def commands(self): # Embed command. embed_cmd = ui.Subcommand( - 'embedart', help=u'embed image files into file metadata' + 'embedart', help='embed image files into file metadata' ) embed_cmd.parser.add_option( - u'-f', u'--file', metavar='PATH', help=u'the image file to embed' + '-f', '--file', metavar='PATH', help='the image file to embed' ) embed_cmd.parser.add_option( - u"-y", u"--yes", action="store_true", help=u"skip confirmation" + "-y", "--yes", action="store_true", help="skip confirmation" ) maxwidth = self.config['maxwidth'].get(int) + quality = self.config['quality'].get(int) compare_threshold = self.config['compare_threshold'].get(int) ifempty = self.config['ifempty'].get(bool) @@ -93,7 +93,7 @@ class EmbedCoverArtPlugin(BeetsPlugin): if opts.file: imagepath = normpath(opts.file) if not os.path.isfile(syspath(imagepath)): - raise ui.UserError(u'image file {0} not found'.format( + raise ui.UserError('image file {} not found'.format( displayable_path(imagepath) )) @@ -104,8 +104,9 @@ class EmbedCoverArtPlugin(BeetsPlugin): return for item in items: - art.embed_item(self._log, item, imagepath, maxwidth, None, - compare_threshold, ifempty) + art.embed_item(self._log, item, imagepath, maxwidth, + None, compare_threshold, ifempty, + quality=quality) else: albums = lib.albums(decargs(args)) @@ -114,8 +115,9 @@ class EmbedCoverArtPlugin(BeetsPlugin): return for album in albums: - art.embed_album(self._log, album, maxwidth, False, - compare_threshold, ifempty) + art.embed_album(self._log, album, maxwidth, + False, compare_threshold, ifempty, + quality=quality) self.remove_artfile(album) embed_cmd.func = embed_func @@ -123,15 +125,15 @@ class EmbedCoverArtPlugin(BeetsPlugin): # Extract command. extract_cmd = ui.Subcommand( 'extractart', - help=u'extract an image from file metadata', + help='extract an image from file metadata', ) extract_cmd.parser.add_option( - u'-o', dest='outpath', - help=u'image output file', + '-o', dest='outpath', + help='image output file', ) extract_cmd.parser.add_option( - u'-n', dest='filename', - help=u'image filename to create for all matched albums', + '-n', dest='filename', + help='image filename to create for all matched albums', ) extract_cmd.parser.add_option( '-a', dest='associate', action='store_true', @@ -147,7 +149,7 @@ class EmbedCoverArtPlugin(BeetsPlugin): config['art_filename'].get()) if os.path.dirname(filename) != b'': self._log.error( - u"Only specify a name rather than a path for -n") + "Only specify a name rather than a path for -n") return for album in lib.albums(decargs(args)): artpath = normpath(os.path.join(album.path, filename)) @@ -161,10 +163,10 @@ class EmbedCoverArtPlugin(BeetsPlugin): # Clear command. clear_cmd = ui.Subcommand( 'clearart', - help=u'remove images from file metadata', + help='remove images from file metadata', ) clear_cmd.parser.add_option( - u"-y", u"--yes", action="store_true", help=u"skip confirmation" + "-y", "--yes", action="store_true", help="skip confirmation" ) def clear_func(lib, opts, args): @@ -189,11 +191,11 @@ class EmbedCoverArtPlugin(BeetsPlugin): def remove_artfile(self, album): """Possibly delete the album art file for an album (if the - appropriate configuration option is enabled. + appropriate configuration option is enabled). """ if self.config['remove_art_file'] and album.artpath: if os.path.isfile(album.artpath): - self._log.debug(u'Removing album art file for {0}', album) + self._log.debug('Removing album art file for {0}', album) os.remove(album.artpath) album.artpath = None album.store() diff --git a/libs/common/beetsplug/embyupdate.py b/libs/common/beetsplug/embyupdate.py index 5c731954..c17fabad 100644 --- a/libs/common/beetsplug/embyupdate.py +++ b/libs/common/beetsplug/embyupdate.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Updates the Emby Library whenever the beets library is changed. emby: @@ -9,14 +7,11 @@ apikey: apikey password: password """ -from __future__ import division, absolute_import, print_function import hashlib import requests -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urljoin, parse_qs, urlsplit, urlunsplit - +from urllib.parse import urlencode, urljoin, parse_qs, urlsplit, urlunsplit from beets import config from beets.plugins import BeetsPlugin @@ -146,14 +141,14 @@ def get_user(host, port, username): class EmbyUpdate(BeetsPlugin): def __init__(self): - super(EmbyUpdate, self).__init__() + super().__init__() # Adding defaults. config['emby'].add({ - u'host': u'http://localhost', - u'port': 8096, - u'apikey': None, - u'password': None, + 'host': 'http://localhost', + 'port': 8096, + 'apikey': None, + 'password': None, }) self.register_listener('database_change', self.listen_for_db_change) @@ -166,7 +161,7 @@ class EmbyUpdate(BeetsPlugin): def update(self, lib): """When the client exists try to send refresh request to Emby. """ - self._log.info(u'Updating Emby library...') + self._log.info('Updating Emby library...') host = config['emby']['host'].get() port = config['emby']['port'].get() @@ -176,13 +171,13 @@ class EmbyUpdate(BeetsPlugin): # Check if at least a apikey or password is given. if not any([password, token]): - self._log.warning(u'Provide at least Emby password or apikey.') + self._log.warning('Provide at least Emby password or apikey.') return # Get user information from the Emby API. user = get_user(host, port, username) if not user: - self._log.warning(u'User {0} could not be found.'.format(username)) + self._log.warning(f'User {username} could not be found.') return if not token: @@ -194,7 +189,7 @@ class EmbyUpdate(BeetsPlugin): token = get_token(host, port, headers, auth_data) if not token: self._log.warning( - u'Could not get token for user {0}', username + 'Could not get token for user {0}', username ) return @@ -205,6 +200,6 @@ class EmbyUpdate(BeetsPlugin): url = api_url(host, port, '/Library/Refresh') r = requests.post(url, headers=headers) if r.status_code != 204: - self._log.warning(u'Update could not be triggered') + self._log.warning('Update could not be triggered') else: - self._log.info(u'Update triggered.') + self._log.info('Update triggered.') diff --git a/libs/common/beetsplug/export.py b/libs/common/beetsplug/export.py index 641b9fef..99f6d706 100644 --- a/libs/common/beetsplug/export.py +++ b/libs/common/beetsplug/export.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # # Permission is hereby granted, free of charge, to any person obtaining @@ -15,23 +14,25 @@ """Exports data from beets """ -from __future__ import division, absolute_import, print_function import sys -import json import codecs +import json +import csv +from xml.etree import ElementTree from datetime import datetime, date from beets.plugins import BeetsPlugin from beets import ui -from beets import mediafile -from beetsplug.info import make_key_filter, library_data, tag_data +from beets import util +import mediafile +from beetsplug.info import library_data, tag_data class ExportEncoder(json.JSONEncoder): """Deals with dates because JSON doesn't have a standard""" def default(self, o): - if isinstance(o, datetime) or isinstance(o, date): + if isinstance(o, (datetime, date)): return o.isoformat() return json.JSONEncoder.default(self, o) @@ -39,12 +40,12 @@ class ExportEncoder(json.JSONEncoder): class ExportPlugin(BeetsPlugin): def __init__(self): - super(ExportPlugin, self).__init__() + super().__init__() self.config.add({ 'default_format': 'json', 'json': { - # json module formatting options + # JSON module formatting options. 'formatting': { 'ensure_ascii': False, 'indent': 4, @@ -52,100 +53,175 @@ class ExportPlugin(BeetsPlugin): 'sort_keys': True } }, + 'jsonlines': { + # JSON Lines formatting options. + 'formatting': { + 'ensure_ascii': False, + 'separators': (',', ': '), + 'sort_keys': True + } + }, + 'csv': { + # CSV module formatting options. + 'formatting': { + # The delimiter used to seperate columns. + 'delimiter': ',', + # The dialect to use when formating the file output. + 'dialect': 'excel' + } + }, + 'xml': { + # XML module formatting options. + 'formatting': {} + } # TODO: Use something like the edit plugin # 'item_fields': [] }) def commands(self): - # TODO: Add option to use albums - - cmd = ui.Subcommand('export', help=u'export data from beets') + cmd = ui.Subcommand('export', help='export data from beets') cmd.func = self.run cmd.parser.add_option( - u'-l', u'--library', action='store_true', - help=u'show library fields instead of tags', + '-l', '--library', action='store_true', + help='show library fields instead of tags', ) cmd.parser.add_option( - u'--append', action='store_true', default=False, - help=u'if should append data to the file', + '-a', '--album', action='store_true', + help='show album fields instead of tracks (implies "--library")', ) cmd.parser.add_option( - u'-i', u'--include-keys', default=[], + '--append', action='store_true', default=False, + help='if should append data to the file', + ) + cmd.parser.add_option( + '-i', '--include-keys', default=[], action='append', dest='included_keys', - help=u'comma separated list of keys to show', + help='comma separated list of keys to show', ) cmd.parser.add_option( - u'-o', u'--output', - help=u'path for the output file. If not given, will print the data' + '-o', '--output', + help='path for the output file. If not given, will print the data' + ) + cmd.parser.add_option( + '-f', '--format', default='json', + help="the output format: json (default), jsonlines, csv, or xml" ) return [cmd] def run(self, lib, opts, args): - file_path = opts.output - file_format = self.config['default_format'].get(str) file_mode = 'a' if opts.append else 'w' + file_format = opts.format or self.config['default_format'].get(str) + file_format_is_line_based = (file_format == 'jsonlines') format_options = self.config[file_format]['formatting'].get(dict) export_format = ExportFormat.factory( - file_format, **{ + file_type=file_format, + **{ 'file_path': file_path, 'file_mode': file_mode } ) - items = [] - data_collector = library_data if opts.library else tag_data + if opts.library or opts.album: + data_collector = library_data + else: + data_collector = tag_data included_keys = [] for keys in opts.included_keys: included_keys.extend(keys.split(',')) - key_filter = make_key_filter(included_keys) - for data_emitter in data_collector(lib, ui.decargs(args)): + items = [] + for data_emitter in data_collector( + lib, ui.decargs(args), + album=opts.album, + ): try: - data, item = data_emitter() - except (mediafile.UnreadableFileError, IOError) as ex: - self._log.error(u'cannot read file: {0}', ex) + data, item = data_emitter(included_keys or '*') + except (mediafile.UnreadableFileError, OSError) as ex: + self._log.error('cannot read file: {0}', ex) continue - data = key_filter(data) - items += [data] + for key, value in data.items(): + if isinstance(value, bytes): + data[key] = util.displayable_path(value) - export_format.export(items, **format_options) - - -class ExportFormat(object): - """The output format type""" - - @classmethod - def factory(cls, type, **kwargs): - if type == "json": - if kwargs['file_path']: - return JsonFileFormat(**kwargs) + if file_format_is_line_based: + export_format.export(data, **format_options) else: - return JsonPrintFormat() - raise NotImplementedError() + items += [data] - def export(self, data, **kwargs): - raise NotImplementedError() + if not file_format_is_line_based: + export_format.export(items, **format_options) -class JsonPrintFormat(ExportFormat): - """Outputs to the console""" - - def export(self, data, **kwargs): - json.dump(data, sys.stdout, cls=ExportEncoder, **kwargs) - - -class JsonFileFormat(ExportFormat): - """Saves in a json file""" - - def __init__(self, file_path, file_mode=u'w', encoding=u'utf-8'): +class ExportFormat: + """The output format type""" + def __init__(self, file_path, file_mode='w', encoding='utf-8'): self.path = file_path self.mode = file_mode self.encoding = encoding + # creates a file object to write/append or sets to stdout + self.out_stream = codecs.open(self.path, self.mode, self.encoding) \ + if self.path else sys.stdout + + @classmethod + def factory(cls, file_type, **kwargs): + if file_type in ["json", "jsonlines"]: + return JsonFormat(**kwargs) + elif file_type == "csv": + return CSVFormat(**kwargs) + elif file_type == "xml": + return XMLFormat(**kwargs) + else: + raise NotImplementedError() def export(self, data, **kwargs): - with codecs.open(self.path, self.mode, self.encoding) as f: - json.dump(data, f, cls=ExportEncoder, **kwargs) + raise NotImplementedError() + + +class JsonFormat(ExportFormat): + """Saves in a json file""" + def __init__(self, file_path, file_mode='w', encoding='utf-8'): + super().__init__(file_path, file_mode, encoding) + + def export(self, data, **kwargs): + json.dump(data, self.out_stream, cls=ExportEncoder, **kwargs) + self.out_stream.write('\n') + + +class CSVFormat(ExportFormat): + """Saves in a csv file""" + def __init__(self, file_path, file_mode='w', encoding='utf-8'): + super().__init__(file_path, file_mode, encoding) + + def export(self, data, **kwargs): + header = list(data[0].keys()) if data else [] + writer = csv.DictWriter(self.out_stream, fieldnames=header, **kwargs) + writer.writeheader() + writer.writerows(data) + + +class XMLFormat(ExportFormat): + """Saves in a xml file""" + def __init__(self, file_path, file_mode='w', encoding='utf-8'): + super().__init__(file_path, file_mode, encoding) + + def export(self, data, **kwargs): + # Creates the XML file structure. + library = ElementTree.Element('library') + tracks = ElementTree.SubElement(library, 'tracks') + if data and isinstance(data[0], dict): + for index, item in enumerate(data): + track = ElementTree.SubElement(tracks, 'track') + for key, value in item.items(): + track_details = ElementTree.SubElement(track, key) + track_details.text = value + # Depending on the version of python the encoding needs to change + try: + data = ElementTree.tostring(library, encoding='unicode', **kwargs) + except LookupError: + data = ElementTree.tostring(library, encoding='utf-8', **kwargs) + + self.out_stream.write(data) diff --git a/libs/common/beetsplug/fetchart.py b/libs/common/beetsplug/fetchart.py index 0e106694..f2c1e5a7 100644 --- a/libs/common/beetsplug/fetchart.py +++ b/libs/common/beetsplug/fetchart.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,12 +14,12 @@ """Fetches album art. """ -from __future__ import division, absolute_import, print_function from contextlib import closing import os import re from tempfile import NamedTemporaryFile +from collections import OrderedDict import requests @@ -29,17 +28,11 @@ from beets import importer from beets import ui from beets import util from beets import config -from beets.mediafile import image_mime_type +from mediafile import image_mime_type from beets.util.artresizer import ArtResizer -from beets.util import confit +from beets.util import sorted_walk from beets.util import syspath, bytestring_path, py3_path -import six - -try: - import itunes - HAVE_ITUNES = True -except ImportError: - HAVE_ITUNES = False +import confuse CONTENT_TYPES = { 'image/jpeg': [b'jpg', b'jpeg'], @@ -48,18 +41,21 @@ CONTENT_TYPES = { IMAGE_EXTENSIONS = [ext for exts in CONTENT_TYPES.values() for ext in exts] -class Candidate(object): +class Candidate: """Holds information about a matching artwork, deals with validation of dimension restrictions and resizing. """ CANDIDATE_BAD = 0 CANDIDATE_EXACT = 1 CANDIDATE_DOWNSCALE = 2 + CANDIDATE_DOWNSIZE = 3 + CANDIDATE_DEINTERLACE = 4 + CANDIDATE_REFORMAT = 5 MATCH_EXACT = 0 MATCH_FALLBACK = 1 - def __init__(self, log, path=None, url=None, source=u'', + def __init__(self, log, path=None, url=None, source='', match=None, size=None): self._log = log self.path = path @@ -75,32 +71,39 @@ class Candidate(object): Return `CANDIDATE_BAD` if the file is unusable. Return `CANDIDATE_EXACT` if the file is usable as-is. - Return `CANDIDATE_DOWNSCALE` if the file must be resized. + Return `CANDIDATE_DOWNSCALE` if the file must be rescaled. + Return `CANDIDATE_DOWNSIZE` if the file must be resized, and possibly + also rescaled. + Return `CANDIDATE_DEINTERLACE` if the file must be deinterlaced. + Return `CANDIDATE_REFORMAT` if the file has to be converted. """ if not self.path: return self.CANDIDATE_BAD - if not (plugin.enforce_ratio or plugin.minwidth or plugin.maxwidth): + if (not (plugin.enforce_ratio or plugin.minwidth or plugin.maxwidth + or plugin.max_filesize or plugin.deinterlace + or plugin.cover_format)): return self.CANDIDATE_EXACT # get_size returns None if no local imaging backend is available if not self.size: self.size = ArtResizer.shared.get_size(self.path) - self._log.debug(u'image size: {}', self.size) + self._log.debug('image size: {}', self.size) if not self.size: - self._log.warning(u'Could not get size of image (please see ' - u'documentation for dependencies). ' - u'The configuration options `minwidth` and ' - u'`enforce_ratio` may be violated.') + self._log.warning('Could not get size of image (please see ' + 'documentation for dependencies). ' + 'The configuration options `minwidth`, ' + '`enforce_ratio` and `max_filesize` ' + 'may be violated.') return self.CANDIDATE_EXACT short_edge = min(self.size) long_edge = max(self.size) - # Check minimum size. + # Check minimum dimension. if plugin.minwidth and self.size[0] < plugin.minwidth: - self._log.debug(u'image too small ({} < {})', + self._log.debug('image too small ({} < {})', self.size[0], plugin.minwidth) return self.CANDIDATE_BAD @@ -109,38 +112,83 @@ class Candidate(object): if plugin.enforce_ratio: if plugin.margin_px: if edge_diff > plugin.margin_px: - self._log.debug(u'image is not close enough to being ' - u'square, ({} - {} > {})', + self._log.debug('image is not close enough to being ' + 'square, ({} - {} > {})', long_edge, short_edge, plugin.margin_px) return self.CANDIDATE_BAD elif plugin.margin_percent: margin_px = plugin.margin_percent * long_edge if edge_diff > margin_px: - self._log.debug(u'image is not close enough to being ' - u'square, ({} - {} > {})', + self._log.debug('image is not close enough to being ' + 'square, ({} - {} > {})', long_edge, short_edge, margin_px) return self.CANDIDATE_BAD elif edge_diff: # also reached for margin_px == 0 and margin_percent == 0.0 - self._log.debug(u'image is not square ({} != {})', + self._log.debug('image is not square ({} != {})', self.size[0], self.size[1]) return self.CANDIDATE_BAD - # Check maximum size. + # Check maximum dimension. + downscale = False if plugin.maxwidth and self.size[0] > plugin.maxwidth: - self._log.debug(u'image needs resizing ({} > {})', + self._log.debug('image needs rescaling ({} > {})', self.size[0], plugin.maxwidth) - return self.CANDIDATE_DOWNSCALE + downscale = True - return self.CANDIDATE_EXACT + # Check filesize. + downsize = False + if plugin.max_filesize: + filesize = os.stat(syspath(self.path)).st_size + if filesize > plugin.max_filesize: + self._log.debug('image needs resizing ({}B > {}B)', + filesize, plugin.max_filesize) + downsize = True + + # Check image format + reformat = False + if plugin.cover_format: + fmt = ArtResizer.shared.get_format(self.path) + reformat = fmt != plugin.cover_format + if reformat: + self._log.debug('image needs reformatting: {} -> {}', + fmt, plugin.cover_format) + + if downscale: + return self.CANDIDATE_DOWNSCALE + elif downsize: + return self.CANDIDATE_DOWNSIZE + elif plugin.deinterlace: + return self.CANDIDATE_DEINTERLACE + elif reformat: + return self.CANDIDATE_REFORMAT + else: + return self.CANDIDATE_EXACT def validate(self, plugin): self.check = self._validate(plugin) return self.check def resize(self, plugin): - if plugin.maxwidth and self.check == self.CANDIDATE_DOWNSCALE: - self.path = ArtResizer.shared.resize(plugin.maxwidth, self.path) + if self.check == self.CANDIDATE_DOWNSCALE: + self.path = \ + ArtResizer.shared.resize(plugin.maxwidth, self.path, + quality=plugin.quality, + max_filesize=plugin.max_filesize) + elif self.check == self.CANDIDATE_DOWNSIZE: + # dimensions are correct, so maxwidth is set to maximum dimension + self.path = \ + ArtResizer.shared.resize(max(self.size), self.path, + quality=plugin.quality, + max_filesize=plugin.max_filesize) + elif self.check == self.CANDIDATE_DEINTERLACE: + self.path = ArtResizer.shared.deinterlace(self.path) + elif self.check == self.CANDIDATE_REFORMAT: + self.path = ArtResizer.shared.reformat( + self.path, + plugin.cover_format, + deinterlaced=plugin.deinterlace, + ) def _logged_get(log, *args, **kwargs): @@ -169,14 +217,19 @@ def _logged_get(log, *args, **kwargs): message = 'getting URL' req = requests.Request('GET', *args, **req_kwargs) + with requests.Session() as s: s.headers = {'User-Agent': 'beets'} prepped = s.prepare_request(req) + settings = s.merge_environment_settings( + prepped.url, {}, None, None, None + ) + send_kwargs.update(settings) log.debug('{}: {}', message, prepped.url) return s.send(prepped, **send_kwargs) -class RequestMixin(object): +class RequestMixin: """Adds a Requests wrapper to the class that uses the logger, which must be named `self._log`. """ @@ -208,10 +261,13 @@ class ArtSource(RequestMixin): def fetch_image(self, candidate, plugin): raise NotImplementedError() + def cleanup(self, candidate): + pass + class LocalArtSource(ArtSource): IS_LOCAL = True - LOC_STR = u'local' + LOC_STR = 'local' def fetch_image(self, candidate, plugin): pass @@ -219,7 +275,7 @@ class LocalArtSource(ArtSource): class RemoteArtSource(ArtSource): IS_LOCAL = False - LOC_STR = u'remote' + LOC_STR = 'remote' def fetch_image(self, candidate, plugin): """Downloads an image from a URL and checks whether it seems to @@ -231,7 +287,7 @@ class RemoteArtSource(ArtSource): candidate.url) try: with closing(self.request(candidate.url, stream=True, - message=u'downloading image')) as resp: + message='downloading image')) as resp: ct = resp.headers.get('Content-Type', None) # Download the image to a temporary file. As some servers @@ -259,16 +315,16 @@ class RemoteArtSource(ArtSource): real_ct = ct if real_ct not in CONTENT_TYPES: - self._log.debug(u'not a supported image: {}', - real_ct or u'unknown content type') + self._log.debug('not a supported image: {}', + real_ct or 'unknown content type') return ext = b'.' + CONTENT_TYPES[real_ct][0] if real_ct != ct: - self._log.warning(u'Server specified {}, but returned a ' - u'{} image. Correcting the extension ' - u'to {}', + self._log.warning('Server specified {}, but returned a ' + '{} image. Correcting the extension ' + 'to {}', ct, real_ct, ext) suffix = py3_path(ext) @@ -278,45 +334,88 @@ class RemoteArtSource(ArtSource): # download the remaining part of the image for chunk in data: fh.write(chunk) - self._log.debug(u'downloaded art to: {0}', + self._log.debug('downloaded art to: {0}', util.displayable_path(fh.name)) candidate.path = util.bytestring_path(fh.name) return - except (IOError, requests.RequestException, TypeError) as exc: + except (OSError, requests.RequestException, TypeError) as exc: # Handling TypeError works around a urllib3 bug: # https://github.com/shazow/urllib3/issues/556 - self._log.debug(u'error fetching art: {}', exc) + self._log.debug('error fetching art: {}', exc) return + def cleanup(self, candidate): + if candidate.path: + try: + util.remove(path=candidate.path) + except util.FilesystemError as exc: + self._log.debug('error cleaning up tmp art: {}', exc) + class CoverArtArchive(RemoteArtSource): - NAME = u"Cover Art Archive" + NAME = "Cover Art Archive" VALID_MATCHING_CRITERIA = ['release', 'releasegroup'] + VALID_THUMBNAIL_SIZES = [250, 500, 1200] - if util.SNI_SUPPORTED: - URL = 'https://coverartarchive.org/release/{mbid}/front' - GROUP_URL = 'https://coverartarchive.org/release-group/{mbid}/front' - else: - URL = 'http://coverartarchive.org/release/{mbid}/front' - GROUP_URL = 'http://coverartarchive.org/release-group/{mbid}/front' + URL = 'https://coverartarchive.org/release/{mbid}' + GROUP_URL = 'https://coverartarchive.org/release-group/{mbid}' def get(self, album, plugin, paths): """Return the Cover Art Archive and Cover Art Archive release group URLs using album MusicBrainz release ID and release group ID. """ + + def get_image_urls(url, size_suffix=None): + try: + response = self.request(url) + except requests.RequestException: + self._log.debug('{}: error receiving response' + .format(self.NAME)) + return + + try: + data = response.json() + except ValueError: + self._log.debug('{}: error loading response: {}' + .format(self.NAME, response.text)) + return + + for item in data.get('images', []): + try: + if 'Front' not in item['types']: + continue + + if size_suffix: + yield item['thumbnails'][size_suffix] + else: + yield item['image'] + except KeyError: + pass + + release_url = self.URL.format(mbid=album.mb_albumid) + release_group_url = self.GROUP_URL.format(mbid=album.mb_releasegroupid) + + # Cover Art Archive API offers pre-resized thumbnails at several sizes. + # If the maxwidth config matches one of the already available sizes + # fetch it directly intead of fetching the full sized image and + # resizing it. + size_suffix = None + if plugin.maxwidth in self.VALID_THUMBNAIL_SIZES: + size_suffix = "-" + str(plugin.maxwidth) + if 'release' in self.match_by and album.mb_albumid: - yield self._candidate(url=self.URL.format(mbid=album.mb_albumid), - match=Candidate.MATCH_EXACT) + for url in get_image_urls(release_url, size_suffix): + yield self._candidate(url=url, match=Candidate.MATCH_EXACT) + if 'releasegroup' in self.match_by and album.mb_releasegroupid: - yield self._candidate( - url=self.GROUP_URL.format(mbid=album.mb_releasegroupid), - match=Candidate.MATCH_FALLBACK) + for url in get_image_urls(release_group_url): + yield self._candidate(url=url, match=Candidate.MATCH_FALLBACK) class Amazon(RemoteArtSource): - NAME = u"Amazon" - URL = 'http://images.amazon.com/images/P/%s.%02i.LZZZZZZZ.jpg' + NAME = "Amazon" + URL = 'https://images.amazon.com/images/P/%s.%02i.LZZZZZZZ.jpg' INDICES = (1, 2) def get(self, album, plugin, paths): @@ -329,8 +428,8 @@ class Amazon(RemoteArtSource): class AlbumArtOrg(RemoteArtSource): - NAME = u"AlbumArt.org scraper" - URL = 'http://www.albumart.org/index_detail.php' + NAME = "AlbumArt.org scraper" + URL = 'https://www.albumart.org/index_detail.php' PAT = r'href\s*=\s*"([^>"]*)"[^>]*title\s*=\s*"View larger image"' def get(self, album, plugin, paths): @@ -341,9 +440,9 @@ class AlbumArtOrg(RemoteArtSource): # Get the page from albumart.org. try: resp = self.request(self.URL, params={'asin': album.asin}) - self._log.debug(u'scraped art URL: {0}', resp.url) + self._log.debug('scraped art URL: {0}', resp.url) except requests.RequestException: - self._log.debug(u'error scraping art page') + self._log.debug('error scraping art page') return # Search the page for the image URL. @@ -352,15 +451,15 @@ class AlbumArtOrg(RemoteArtSource): image_url = m.group(1) yield self._candidate(url=image_url, match=Candidate.MATCH_EXACT) else: - self._log.debug(u'no image found on page') + self._log.debug('no image found on page') class GoogleImages(RemoteArtSource): - NAME = u"Google Images" - URL = u'https://www.googleapis.com/customsearch/v1' + NAME = "Google Images" + URL = 'https://www.googleapis.com/customsearch/v1' def __init__(self, *args, **kwargs): - super(GoogleImages, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.key = self._config['google_key'].get(), self.cx = self._config['google_engine'].get(), @@ -371,24 +470,29 @@ class GoogleImages(RemoteArtSource): if not (album.albumartist and album.album): return search_string = (album.albumartist + ',' + album.album).encode('utf-8') - response = self.request(self.URL, params={ - 'key': self.key, - 'cx': self.cx, - 'q': search_string, - 'searchType': 'image' - }) + + try: + response = self.request(self.URL, params={ + 'key': self.key, + 'cx': self.cx, + 'q': search_string, + 'searchType': 'image' + }) + except requests.RequestException: + self._log.debug('google: error receiving response') + return # Get results using JSON. try: data = response.json() except ValueError: - self._log.debug(u'google: error loading response: {}' + self._log.debug('google: error loading response: {}' .format(response.text)) return if 'error' in data: reason = data['error']['errors'][0]['reason'] - self._log.debug(u'google fetchart error: {0}', reason) + self._log.debug('google fetchart error: {0}', reason) return if 'items' in data.keys(): @@ -399,103 +503,142 @@ class GoogleImages(RemoteArtSource): class FanartTV(RemoteArtSource): """Art from fanart.tv requested using their API""" - NAME = u"fanart.tv" + NAME = "fanart.tv" API_URL = 'https://webservice.fanart.tv/v3/' API_ALBUMS = API_URL + 'music/albums/' PROJECT_KEY = '61a7d0ab4e67162b7a0c7c35915cd48e' def __init__(self, *args, **kwargs): - super(FanartTV, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.client_key = self._config['fanarttv_key'].get() def get(self, album, plugin, paths): if not album.mb_releasegroupid: return - response = self.request( - self.API_ALBUMS + album.mb_releasegroupid, - headers={'api-key': self.PROJECT_KEY, - 'client-key': self.client_key}) + try: + response = self.request( + self.API_ALBUMS + album.mb_releasegroupid, + headers={'api-key': self.PROJECT_KEY, + 'client-key': self.client_key}) + except requests.RequestException: + self._log.debug('fanart.tv: error receiving response') + return try: data = response.json() except ValueError: - self._log.debug(u'fanart.tv: error loading response: {}', + self._log.debug('fanart.tv: error loading response: {}', response.text) return - if u'status' in data and data[u'status'] == u'error': - if u'not found' in data[u'error message'].lower(): - self._log.debug(u'fanart.tv: no image found') - elif u'api key' in data[u'error message'].lower(): - self._log.warning(u'fanart.tv: Invalid API key given, please ' - u'enter a valid one in your config file.') + if 'status' in data and data['status'] == 'error': + if 'not found' in data['error message'].lower(): + self._log.debug('fanart.tv: no image found') + elif 'api key' in data['error message'].lower(): + self._log.warning('fanart.tv: Invalid API key given, please ' + 'enter a valid one in your config file.') else: - self._log.debug(u'fanart.tv: error on request: {}', - data[u'error message']) + self._log.debug('fanart.tv: error on request: {}', + data['error message']) return matches = [] # can there be more than one releasegroupid per response? - for mbid, art in data.get(u'albums', dict()).items(): + for mbid, art in data.get('albums', {}).items(): # there might be more art referenced, e.g. cdart, and an albumcover - # might not be present, even if the request was succesful - if album.mb_releasegroupid == mbid and u'albumcover' in art: - matches.extend(art[u'albumcover']) + # might not be present, even if the request was successful + if album.mb_releasegroupid == mbid and 'albumcover' in art: + matches.extend(art['albumcover']) # can this actually occur? else: - self._log.debug(u'fanart.tv: unexpected mb_releasegroupid in ' - u'response!') + self._log.debug('fanart.tv: unexpected mb_releasegroupid in ' + 'response!') - matches.sort(key=lambda x: x[u'likes'], reverse=True) + matches.sort(key=lambda x: x['likes'], reverse=True) for item in matches: # fanart.tv has a strict size requirement for album art to be # uploaded - yield self._candidate(url=item[u'url'], + yield self._candidate(url=item['url'], match=Candidate.MATCH_EXACT, size=(1000, 1000)) class ITunesStore(RemoteArtSource): - NAME = u"iTunes Store" + NAME = "iTunes Store" + API_URL = 'https://itunes.apple.com/search' def get(self, album, plugin, paths): """Return art URL from iTunes Store given an album title. """ if not (album.albumartist and album.album): return - search_string = (album.albumartist + ' ' + album.album).encode('utf-8') + + payload = { + 'term': album.albumartist + ' ' + album.album, + 'entity': 'album', + 'media': 'music', + 'limit': 200 + } try: - # Isolate bugs in the iTunes library while searching. + r = self.request(self.API_URL, params=payload) + r.raise_for_status() + except requests.RequestException as e: + self._log.debug('iTunes search failed: {0}', e) + return + + try: + candidates = r.json()['results'] + except ValueError as e: + self._log.debug('Could not decode json response: {0}', e) + return + except KeyError as e: + self._log.debug('{} not found in json. Fields are {} ', + e, + list(r.json().keys())) + return + + if not candidates: + self._log.debug('iTunes search for {!r} got no results', + payload['term']) + return + + if self._config['high_resolution']: + image_suffix = '100000x100000-999' + else: + image_suffix = '1200x1200bb' + + for c in candidates: try: - results = itunes.search_album(search_string) - except Exception as exc: - self._log.debug(u'iTunes search failed: {0}', exc) - return + if (c['artistName'] == album.albumartist + and c['collectionName'] == album.album): + art_url = c['artworkUrl100'] + art_url = art_url.replace('100x100bb', + image_suffix) + yield self._candidate(url=art_url, + match=Candidate.MATCH_EXACT) + except KeyError as e: + self._log.debug('Malformed itunes candidate: {} not found in {}', # NOQA E501 + e, + list(c.keys())) - # Get the first match. - if results: - itunes_album = results[0] - else: - self._log.debug(u'iTunes search for {:r} got no results', - search_string) - return - - if itunes_album.get_artwork()['100']: - small_url = itunes_album.get_artwork()['100'] - big_url = small_url.replace('100x100', '1200x1200') - yield self._candidate(url=big_url, match=Candidate.MATCH_EXACT) - else: - self._log.debug(u'album has no artwork in iTunes Store') - except IndexError: - self._log.debug(u'album not found in iTunes Store') + try: + fallback_art_url = candidates[0]['artworkUrl100'] + fallback_art_url = fallback_art_url.replace('100x100bb', + image_suffix) + yield self._candidate(url=fallback_art_url, + match=Candidate.MATCH_FALLBACK) + except KeyError as e: + self._log.debug('Malformed itunes candidate: {} not found in {}', + e, + list(c.keys())) class Wikipedia(RemoteArtSource): - NAME = u"Wikipedia (queried through DBpedia)" + NAME = "Wikipedia (queried through DBpedia)" DBPEDIA_URL = 'https://dbpedia.org/sparql' WIKIPEDIA_URL = 'https://en.wikipedia.org/w/api.php' - SPARQL_QUERY = u'''PREFIX rdf: + SPARQL_QUERY = '''PREFIX rdf: PREFIX dbpprop: PREFIX owl: PREFIX rdfs: @@ -523,16 +666,22 @@ class Wikipedia(RemoteArtSource): # Find the name of the cover art filename on DBpedia cover_filename, page_id = None, None - dbpedia_response = self.request( - self.DBPEDIA_URL, - params={ - 'format': 'application/sparql-results+json', - 'timeout': 2500, - 'query': self.SPARQL_QUERY.format( - artist=album.albumartist.title(), album=album.album) - }, - headers={'content-type': 'application/json'}, - ) + + try: + dbpedia_response = self.request( + self.DBPEDIA_URL, + params={ + 'format': 'application/sparql-results+json', + 'timeout': 2500, + 'query': self.SPARQL_QUERY.format( + artist=album.albumartist.title(), album=album.album) + }, + headers={'content-type': 'application/json'}, + ) + except requests.RequestException: + self._log.debug('dbpedia: error receiving response') + return + try: data = dbpedia_response.json() results = data['results']['bindings'] @@ -540,9 +689,9 @@ class Wikipedia(RemoteArtSource): cover_filename = 'File:' + results[0]['coverFilename']['value'] page_id = results[0]['pageId']['value'] else: - self._log.debug(u'wikipedia: album not found on dbpedia') + self._log.debug('wikipedia: album not found on dbpedia') except (ValueError, KeyError, IndexError): - self._log.debug(u'wikipedia: error scraping dbpedia response: {}', + self._log.debug('wikipedia: error scraping dbpedia response: {}', dbpedia_response.text) # Ensure we have a filename before attempting to query wikipedia @@ -557,25 +706,29 @@ class Wikipedia(RemoteArtSource): if ' .' in cover_filename and \ '.' not in cover_filename.split(' .')[-1]: self._log.debug( - u'wikipedia: dbpedia provided incomplete cover_filename' + 'wikipedia: dbpedia provided incomplete cover_filename' ) lpart, rpart = cover_filename.rsplit(' .', 1) # Query all the images in the page - wikipedia_response = self.request( - self.WIKIPEDIA_URL, - params={ - 'format': 'json', - 'action': 'query', - 'continue': '', - 'prop': 'images', - 'pageids': page_id, - }, - headers={'content-type': 'application/json'}, - ) + try: + wikipedia_response = self.request( + self.WIKIPEDIA_URL, + params={ + 'format': 'json', + 'action': 'query', + 'continue': '', + 'prop': 'images', + 'pageids': page_id, + }, + headers={'content-type': 'application/json'}, + ) + except requests.RequestException: + self._log.debug('wikipedia: error receiving response') + return # Try to see if one of the images on the pages matches our - # imcomplete cover_filename + # incomplete cover_filename try: data = wikipedia_response.json() results = data['query']['pages'][page_id]['images'] @@ -586,23 +739,27 @@ class Wikipedia(RemoteArtSource): break except (ValueError, KeyError): self._log.debug( - u'wikipedia: failed to retrieve a cover_filename' + 'wikipedia: failed to retrieve a cover_filename' ) return # Find the absolute url of the cover art on Wikipedia - wikipedia_response = self.request( - self.WIKIPEDIA_URL, - params={ - 'format': 'json', - 'action': 'query', - 'continue': '', - 'prop': 'imageinfo', - 'iiprop': 'url', - 'titles': cover_filename.encode('utf-8'), - }, - headers={'content-type': 'application/json'}, - ) + try: + wikipedia_response = self.request( + self.WIKIPEDIA_URL, + params={ + 'format': 'json', + 'action': 'query', + 'continue': '', + 'prop': 'imageinfo', + 'iiprop': 'url', + 'titles': cover_filename.encode('utf-8'), + }, + headers={'content-type': 'application/json'}, + ) + except requests.RequestException: + self._log.debug('wikipedia: error receiving response') + return try: data = wikipedia_response.json() @@ -612,12 +769,12 @@ class Wikipedia(RemoteArtSource): yield self._candidate(url=image_url, match=Candidate.MATCH_EXACT) except (ValueError, KeyError, IndexError): - self._log.debug(u'wikipedia: error scraping imageinfo') + self._log.debug('wikipedia: error scraping imageinfo') return class FileSystem(LocalArtSource): - NAME = u"Filesystem" + NAME = "Filesystem" @staticmethod def filename_priority(filename, cover_names): @@ -644,12 +801,16 @@ class FileSystem(LocalArtSource): # Find all files that look like images in the directory. images = [] - for fn in os.listdir(syspath(path)): - fn = bytestring_path(fn) - for ext in IMAGE_EXTENSIONS: - if fn.lower().endswith(b'.' + ext) and \ - os.path.isfile(syspath(os.path.join(path, fn))): - images.append(fn) + ignore = config['ignore'].as_str_seq() + ignore_hidden = config['ignore_hidden'].get(bool) + for _, _, files in sorted_walk(path, ignore=ignore, + ignore_hidden=ignore_hidden): + for fn in files: + fn = bytestring_path(fn) + for ext in IMAGE_EXTENSIONS: + if fn.lower().endswith(b'.' + ext) and \ + os.path.isfile(syspath(os.path.join(path, fn))): + images.append(fn) # Look for "preferred" filenames. images = sorted(images, @@ -658,7 +819,7 @@ class FileSystem(LocalArtSource): remaining = [] for fn in images: if re.search(cover_pat, os.path.splitext(fn)[0], re.I): - self._log.debug(u'using well-named art file {0}', + self._log.debug('using well-named art file {0}', util.displayable_path(fn)) yield self._candidate(path=os.path.join(path, fn), match=Candidate.MATCH_EXACT) @@ -667,27 +828,86 @@ class FileSystem(LocalArtSource): # Fall back to any image in the folder. if remaining and not plugin.cautious: - self._log.debug(u'using fallback art file {0}', + self._log.debug('using fallback art file {0}', util.displayable_path(remaining[0])) yield self._candidate(path=os.path.join(path, remaining[0]), match=Candidate.MATCH_FALLBACK) +class LastFM(RemoteArtSource): + NAME = "Last.fm" + + # Sizes in priority order. + SIZES = OrderedDict([ + ('mega', (300, 300)), + ('extralarge', (300, 300)), + ('large', (174, 174)), + ('medium', (64, 64)), + ('small', (34, 34)), + ]) + + API_URL = 'https://ws.audioscrobbler.com/2.0' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.key = self._config['lastfm_key'].get(), + + def get(self, album, plugin, paths): + if not album.mb_albumid: + return + + try: + response = self.request(self.API_URL, params={ + 'method': 'album.getinfo', + 'api_key': self.key, + 'mbid': album.mb_albumid, + 'format': 'json', + }) + except requests.RequestException: + self._log.debug('lastfm: error receiving response') + return + + try: + data = response.json() + + if 'error' in data: + if data['error'] == 6: + self._log.debug('lastfm: no results for {}', + album.mb_albumid) + else: + self._log.error( + 'lastfm: failed to get album info: {} ({})', + data['message'], data['error']) + else: + images = {image['size']: image['#text'] + for image in data['album']['image']} + + # Provide candidates in order of size. + for size in self.SIZES.keys(): + if size in images: + yield self._candidate(url=images[size], + size=self.SIZES[size]) + except ValueError: + self._log.debug('lastfm: error loading response: {}' + .format(response.text)) + return + # Try each source in turn. -SOURCES_ALL = [u'filesystem', - u'coverart', u'itunes', u'amazon', u'albumart', - u'wikipedia', u'google', u'fanarttv'] +SOURCES_ALL = ['filesystem', + 'coverart', 'itunes', 'amazon', 'albumart', + 'wikipedia', 'google', 'fanarttv', 'lastfm'] ART_SOURCES = { - u'filesystem': FileSystem, - u'coverart': CoverArtArchive, - u'itunes': ITunesStore, - u'albumart': AlbumArtOrg, - u'amazon': Amazon, - u'wikipedia': Wikipedia, - u'google': GoogleImages, - u'fanarttv': FanartTV, + 'filesystem': FileSystem, + 'coverart': CoverArtArchive, + 'itunes': ITunesStore, + 'albumart': AlbumArtOrg, + 'amazon': Amazon, + 'wikipedia': Wikipedia, + 'google': GoogleImages, + 'fanarttv': FanartTV, + 'lastfm': LastFM, } SOURCE_NAMES = {v: k for k, v in ART_SOURCES.items()} @@ -699,7 +919,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): PAT_PERCENT = r"(100(\.00?)?|[1-9]?[0-9](\.[0-9]{1,2})?)%" def __init__(self): - super(FetchArtPlugin, self).__init__() + super().__init__() # Holds candidates corresponding to downloaded images between # fetching them and placing them in the filesystem. @@ -709,37 +929,47 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): 'auto': True, 'minwidth': 0, 'maxwidth': 0, + 'quality': 0, + 'max_filesize': 0, 'enforce_ratio': False, 'cautious': False, 'cover_names': ['cover', 'front', 'art', 'album', 'folder'], 'sources': ['filesystem', 'coverart', 'itunes', 'amazon', 'albumart'], 'google_key': None, - 'google_engine': u'001442825323518660753:hrh5ch1gjzm', + 'google_engine': '001442825323518660753:hrh5ch1gjzm', 'fanarttv_key': None, + 'lastfm_key': None, 'store_source': False, + 'high_resolution': False, + 'deinterlace': False, + 'cover_format': None, }) self.config['google_key'].redact = True self.config['fanarttv_key'].redact = True + self.config['lastfm_key'].redact = True self.minwidth = self.config['minwidth'].get(int) self.maxwidth = self.config['maxwidth'].get(int) + self.max_filesize = self.config['max_filesize'].get(int) + self.quality = self.config['quality'].get(int) # allow both pixel and percentage-based margin specifications self.enforce_ratio = self.config['enforce_ratio'].get( - confit.OneOf([bool, - confit.String(pattern=self.PAT_PX), - confit.String(pattern=self.PAT_PERCENT)])) + confuse.OneOf([bool, + confuse.String(pattern=self.PAT_PX), + confuse.String(pattern=self.PAT_PERCENT)])) self.margin_px = None self.margin_percent = None - if type(self.enforce_ratio) is six.text_type: - if self.enforce_ratio[-1] == u'%': + self.deinterlace = self.config['deinterlace'].get(bool) + if type(self.enforce_ratio) is str: + if self.enforce_ratio[-1] == '%': self.margin_percent = float(self.enforce_ratio[:-1]) / 100 - elif self.enforce_ratio[-2:] == u'px': + elif self.enforce_ratio[-2:] == 'px': self.margin_px = int(self.enforce_ratio[:-2]) else: # shouldn't happen - raise confit.ConfigValueError() + raise confuse.ConfigValueError() self.enforce_ratio = True cover_names = self.config['cover_names'].as_str_seq() @@ -750,17 +980,22 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): self.src_removed = (config['import']['delete'].get(bool) or config['import']['move'].get(bool)) + self.cover_format = self.config['cover_format'].get( + confuse.Optional(str) + ) + if self.config['auto']: # Enable two import hooks when fetching is enabled. self.import_stages = [self.fetch_art] self.register_listener('import_task_files', self.assign_art) available_sources = list(SOURCES_ALL) - if not HAVE_ITUNES and u'itunes' in available_sources: - available_sources.remove(u'itunes') if not self.config['google_key'].get() and \ - u'google' in available_sources: - available_sources.remove(u'google') + 'google' in available_sources: + available_sources.remove('google') + if not self.config['lastfm_key'].get() and \ + 'lastfm' in available_sources: + available_sources.remove('lastfm') available_sources = [(s, c) for s in available_sources for c in ART_SOURCES[s].VALID_MATCHING_CRITERIA] @@ -770,9 +1005,9 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): if 'remote_priority' in self.config: self._log.warning( - u'The `fetch_art.remote_priority` configuration option has ' - u'been deprecated. Instead, place `filesystem` at the end of ' - u'your `sources` list.') + 'The `fetch_art.remote_priority` configuration option has ' + 'been deprecated. Instead, place `filesystem` at the end of ' + 'your `sources` list.') if self.config['remote_priority'].get(bool): fs = [] others = [] @@ -814,7 +1049,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): if self.store_source: # store the source of the chosen artwork in a flexible field self._log.debug( - u"Storing art_source for {0.albumartist} - {0.album}", + "Storing art_source for {0.albumartist} - {0.album}", album) album.art_source = SOURCE_NAMES[type(candidate.source)] album.store() @@ -834,14 +1069,14 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): def commands(self): cmd = ui.Subcommand('fetchart', help='download album art') cmd.parser.add_option( - u'-f', u'--force', dest='force', + '-f', '--force', dest='force', action='store_true', default=False, - help=u're-download art when already present' + help='re-download art when already present' ) cmd.parser.add_option( - u'-q', u'--quiet', dest='quiet', + '-q', '--quiet', dest='quiet', action='store_true', default=False, - help=u'shows only quiet art' + help='quiet mode: do not output albums that already have artwork' ) def func(lib, opts, args): @@ -855,16 +1090,17 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): def art_for_album(self, album, paths, local_only=False): """Given an Album object, returns a path to downloaded art for the album (or None if no art is found). If `maxwidth`, then images are - resized to this maximum pixel size. If `local_only`, then only local - image files from the filesystem are returned; no network requests - are made. + resized to this maximum pixel size. If `quality` then resized images + are saved at the specified quality level. If `local_only`, then only + local image files from the filesystem are returned; no network + requests are made. """ out = None for source in self.sources: if source.IS_LOCAL or not local_only: self._log.debug( - u'trying source {0} for album {1.albumartist} - {1.album}', + 'trying source {0} for album {1.albumartist} - {1.album}', SOURCE_NAMES[type(source)], album, ) @@ -875,9 +1111,11 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): if candidate.validate(self): out = candidate self._log.debug( - u'using {0.LOC_STR} image {1}'.format( + 'using {0.LOC_STR} image {1}'.format( source, util.displayable_path(out.path))) break + # Remove temporary files for invalid candidates. + source.cleanup(candidate) if out: break @@ -894,8 +1132,8 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): if album.artpath and not force and os.path.isfile(album.artpath): if not quiet: message = ui.colorize('text_highlight_minor', - u'has album art') - self._log.info(u'{0}: {1}', album, message) + 'has album art') + self._log.info('{0}: {1}', album, message) else: # In ordinary invocations, look for images on the # filesystem. When forcing, however, always go to the Web @@ -905,7 +1143,7 @@ class FetchArtPlugin(plugins.BeetsPlugin, RequestMixin): candidate = self.art_for_album(album, local_paths) if candidate: self._set_art(album, candidate) - message = ui.colorize('text_success', u'found album art') + message = ui.colorize('text_success', 'found album art') else: - message = ui.colorize('text_error', u'no art found') - self._log.info(u'{0}: {1}', album, message) + message = ui.colorize('text_error', 'no art found') + self._log.info('{0}: {1}', album, message) diff --git a/libs/common/beetsplug/filefilter.py b/libs/common/beetsplug/filefilter.py index 23dac574..ec8fddb4 100644 --- a/libs/common/beetsplug/filefilter.py +++ b/libs/common/beetsplug/filefilter.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Malte Ried. # @@ -16,7 +15,6 @@ """Filter imported files using a regular expression. """ -from __future__ import division, absolute_import, print_function import re from beets import config @@ -27,7 +25,7 @@ from beets.importer import SingletonImportTask class FileFilterPlugin(BeetsPlugin): def __init__(self): - super(FileFilterPlugin, self).__init__() + super().__init__() self.register_listener('import_task_created', self.import_task_created_event) self.config.add({ @@ -43,8 +41,8 @@ class FileFilterPlugin(BeetsPlugin): bytestring_path(self.config['album_path'].get())) if 'singleton_path' in self.config: - self.path_singleton_regex = re.compile( - bytestring_path(self.config['singleton_path'].get())) + self.path_singleton_regex = re.compile( + bytestring_path(self.config['singleton_path'].get())) def import_task_created_event(self, session, task): if task.items and len(task.items) > 0: diff --git a/libs/common/beetsplug/fish.py b/libs/common/beetsplug/fish.py new file mode 100644 index 00000000..21fd67f6 --- /dev/null +++ b/libs/common/beetsplug/fish.py @@ -0,0 +1,285 @@ +# This file is part of beets. +# Copyright 2015, winters jean-marie. +# Copyright 2020, Justin Mayer +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""This plugin generates tab completions for Beets commands for the Fish shell +, including completions for Beets commands, plugin +commands, and option flags. Also generated are completions for all the album +and track fields, suggesting for example `genre:` or `album:` when querying the +Beets database. Completions for the *values* of those fields are not generated +by default but can be added via the `-e` / `--extravalues` flag. For example: +`beet fish -e genre -e albumartist` +""" + + +from beets.plugins import BeetsPlugin +from beets import library, ui +from beets.ui import commands +from operator import attrgetter +import os +BL_NEED2 = """complete -c beet -n '__fish_beet_needs_command' {} {}\n""" +BL_USE3 = """complete -c beet -n '__fish_beet_using_command {}' {} {}\n""" +BL_SUBS = """complete -c beet -n '__fish_at_level {} ""' {} {}\n""" +BL_EXTRA3 = """complete -c beet -n '__fish_beet_use_extra {}' {} {}\n""" + +HEAD = ''' +function __fish_beet_needs_command + set cmd (commandline -opc) + if test (count $cmd) -eq 1 + return 0 + end + return 1 +end + +function __fish_beet_using_command + set cmd (commandline -opc) + set needle (count $cmd) + if test $needle -gt 1 + if begin test $argv[1] = $cmd[2]; + and not contains -- $cmd[$needle] $FIELDS; end + return 0 + end + end + return 1 +end + +function __fish_beet_use_extra + set cmd (commandline -opc) + set needle (count $cmd) + if test $argv[2] = $cmd[$needle] + return 0 + end + return 1 +end +''' + + +class FishPlugin(BeetsPlugin): + + def commands(self): + cmd = ui.Subcommand('fish', help='generate Fish shell tab completions') + cmd.func = self.run + cmd.parser.add_option('-f', '--noFields', action='store_true', + default=False, + help='omit album/track field completions') + cmd.parser.add_option( + '-e', + '--extravalues', + action='append', + type='choice', + choices=library.Item.all_keys() + + library.Album.all_keys(), + help='include specified field *values* in completions') + return [cmd] + + def run(self, lib, opts, args): + # Gather the commands from Beets core and its plugins. + # Collect the album and track fields. + # If specified, also collect the values for these fields. + # Make a giant string of all the above, formatted in a way that + # allows Fish to do tab completion for the `beet` command. + home_dir = os.path.expanduser("~") + completion_dir = os.path.join(home_dir, '.config/fish/completions') + try: + os.makedirs(completion_dir) + except OSError: + if not os.path.isdir(completion_dir): + raise + completion_file_path = os.path.join(completion_dir, 'beet.fish') + nobasicfields = opts.noFields # Do not complete for album/track fields + extravalues = opts.extravalues # e.g., Also complete artists names + beetcmds = sorted( + (commands.default_commands + + commands.plugins.commands()), + key=attrgetter('name')) + fields = sorted(set( + library.Album.all_keys() + library.Item.all_keys())) + # Collect commands, their aliases, and their help text + cmd_names_help = [] + for cmd in beetcmds: + names = list(cmd.aliases) + names.append(cmd.name) + for name in names: + cmd_names_help.append((name, cmd.help)) + # Concatenate the string + totstring = HEAD + "\n" + totstring += get_cmds_list([name[0] for name in cmd_names_help]) + totstring += '' if nobasicfields else get_standard_fields(fields) + totstring += get_extravalues(lib, extravalues) if extravalues else '' + totstring += "\n" + "# ====== {} =====".format( + "setup basic beet completion") + "\n" * 2 + totstring += get_basic_beet_options() + totstring += "\n" + "# ====== {} =====".format( + "setup field completion for subcommands") + "\n" + totstring += get_subcommands( + cmd_names_help, nobasicfields, extravalues) + # Set up completion for all the command options + totstring += get_all_commands(beetcmds) + + with open(completion_file_path, 'w') as fish_file: + fish_file.write(totstring) + + +def _escape(name): + # Escape ? in fish + if name == "?": + name = "\\" + name + return name + + +def get_cmds_list(cmds_names): + # Make a list of all Beets core & plugin commands + substr = '' + substr += ( + "set CMDS " + " ".join(cmds_names) + ("\n" * 2) + ) + return substr + + +def get_standard_fields(fields): + # Make a list of album/track fields and append with ':' + fields = (field + ":" for field in fields) + substr = '' + substr += ( + "set FIELDS " + " ".join(fields) + ("\n" * 2) + ) + return substr + + +def get_extravalues(lib, extravalues): + # Make a list of all values from an album/track field. + # 'beet ls albumartist: ' yields completions for ABBA, Beatles, etc. + word = '' + values_set = get_set_of_values_for_field(lib, extravalues) + for fld in extravalues: + extraname = fld.upper() + 'S' + word += ( + "set " + extraname + " " + " ".join(sorted(values_set[fld])) + + ("\n" * 2) + ) + return word + + +def get_set_of_values_for_field(lib, fields): + # Get unique values from a specified album/track field + fields_dict = {} + for each in fields: + fields_dict[each] = set() + for item in lib.items(): + for field in fields: + fields_dict[field].add(wrap(item[field])) + return fields_dict + + +def get_basic_beet_options(): + word = ( + BL_NEED2.format("-l format-item", + "-f -d 'print with custom format'") + + BL_NEED2.format("-l format-album", + "-f -d 'print with custom format'") + + BL_NEED2.format("-s l -l library", + "-f -r -d 'library database file to use'") + + BL_NEED2.format("-s d -l directory", + "-f -r -d 'destination music directory'") + + BL_NEED2.format("-s v -l verbose", + "-f -d 'print debugging information'") + + + BL_NEED2.format("-s c -l config", + "-f -r -d 'path to configuration file'") + + BL_NEED2.format("-s h -l help", + "-f -d 'print this help message and exit'")) + return word + + +def get_subcommands(cmd_name_and_help, nobasicfields, extravalues): + # Formatting for Fish to complete our fields/values + word = "" + for cmdname, cmdhelp in cmd_name_and_help: + cmdname = _escape(cmdname) + + word += "\n" + "# ------ {} -------".format( + "fieldsetups for " + cmdname) + "\n" + word += ( + BL_NEED2.format( + ("-a " + cmdname), + ("-f " + "-d " + wrap(clean_whitespace(cmdhelp))))) + + if nobasicfields is False: + word += ( + BL_USE3.format( + cmdname, + ("-a " + wrap("$FIELDS")), + ("-f " + "-d " + wrap("fieldname")))) + + if extravalues: + for f in extravalues: + setvar = wrap("$" + f.upper() + "S") + word += " ".join(BL_EXTRA3.format( + (cmdname + " " + f + ":"), + ('-f ' + '-A ' + '-a ' + setvar), + ('-d ' + wrap(f))).split()) + "\n" + return word + + +def get_all_commands(beetcmds): + # Formatting for Fish to complete command options + word = "" + for cmd in beetcmds: + names = list(cmd.aliases) + names.append(cmd.name) + for name in names: + name = _escape(name) + + word += "\n" + word += ("\n" * 2) + "# ====== {} =====".format( + "completions for " + name) + "\n" + + for option in cmd.parser._get_all_options()[1:]: + cmd_l = (" -l " + option._long_opts[0].replace('--', '') + )if option._long_opts else '' + cmd_s = (" -s " + option._short_opts[0].replace('-', '') + ) if option._short_opts else '' + cmd_need_arg = ' -r ' if option.nargs in [1] else '' + cmd_helpstr = (" -d " + wrap(' '.join(option.help.split())) + ) if option.help else '' + cmd_arglist = (' -a ' + wrap(" ".join(option.choices)) + ) if option.choices else '' + + word += " ".join(BL_USE3.format( + name, + (cmd_need_arg + cmd_s + cmd_l + " -f " + cmd_arglist), + cmd_helpstr).split()) + "\n" + + word = (word + " ".join(BL_USE3.format( + name, + ("-s " + "h " + "-l " + "help" + " -f "), + ('-d ' + wrap("print help") + "\n") + ).split())) + return word + + +def clean_whitespace(word): + # Remove excess whitespace and tabs in a string + return " ".join(word.split()) + + +def wrap(word): + # Need " or ' around strings but watch out if they're in the string + sptoken = '\"' + if ('"') in word and ("'") in word: + word.replace('"', sptoken) + return '"' + word + '"' + + tok = '"' if "'" in word else "'" + return tok + word + tok diff --git a/libs/common/beetsplug/freedesktop.py b/libs/common/beetsplug/freedesktop.py index a768be2d..ba4d5879 100644 --- a/libs/common/beetsplug/freedesktop.py +++ b/libs/common/beetsplug/freedesktop.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Matt Lichtenberg. # @@ -16,7 +15,6 @@ """Creates freedesktop.org-compliant .directory files on an album level. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui @@ -26,12 +24,12 @@ class FreedesktopPlugin(BeetsPlugin): def commands(self): deprecated = ui.Subcommand( "freedesktop", - help=u"Print a message to redirect to thumbnails --dolphin") + help="Print a message to redirect to thumbnails --dolphin") deprecated.func = self.deprecation_message return [deprecated] def deprecation_message(self, lib, opts, args): - ui.print_(u"This plugin is deprecated. Its functionality is " - u"superseded by the 'thumbnails' plugin") - ui.print_(u"'thumbnails --dolphin' replaces freedesktop. See doc & " - u"changelog for more information") + ui.print_("This plugin is deprecated. Its functionality is " + "superseded by the 'thumbnails' plugin") + ui.print_("'thumbnails --dolphin' replaces freedesktop. See doc & " + "changelog for more information") diff --git a/libs/common/beetsplug/fromfilename.py b/libs/common/beetsplug/fromfilename.py index 56b68f75..55684a27 100644 --- a/libs/common/beetsplug/fromfilename.py +++ b/libs/common/beetsplug/fromfilename.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Jan-Erik Dahlin # @@ -16,13 +15,11 @@ """If the title is empty, try to extract track and title from the filename. """ -from __future__ import division, absolute_import, print_function from beets import plugins from beets.util import displayable_path import os import re -import six # Filename field extraction patterns. @@ -124,7 +121,7 @@ def apply_matches(d): # Apply the title and track. for item in d: if bad_title(item.title): - item.title = six.text_type(d[item][title_field]) + item.title = str(d[item][title_field]) if 'track' in d[item] and item.track == 0: item.track = int(d[item]['track']) @@ -133,7 +130,7 @@ def apply_matches(d): class FromFilenamePlugin(plugins.BeetsPlugin): def __init__(self): - super(FromFilenamePlugin, self).__init__() + super().__init__() self.register_listener('import_task_start', filename_task) diff --git a/libs/common/beetsplug/ftintitle.py b/libs/common/beetsplug/ftintitle.py index 9303f9cf..57863d2b 100644 --- a/libs/common/beetsplug/ftintitle.py +++ b/libs/common/beetsplug/ftintitle.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Verrus, # @@ -15,7 +14,6 @@ """Moves "featured" artists to the title from the artist field. """ -from __future__ import division, absolute_import, print_function import re @@ -75,22 +73,22 @@ def find_feat_part(artist, albumartist): class FtInTitlePlugin(plugins.BeetsPlugin): def __init__(self): - super(FtInTitlePlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, 'drop': False, - 'format': u'feat. {0}', + 'format': 'feat. {0}', }) self._command = ui.Subcommand( 'ftintitle', - help=u'move featured artists to the title field') + help='move featured artists to the title field') self._command.parser.add_option( - u'-d', u'--drop', dest='drop', + '-d', '--drop', dest='drop', action='store_true', default=None, - help=u'drop featuring from artists and ignore title update') + help='drop featuring from artists and ignore title update') if self.config['auto']: self.import_stages = [self.imported] @@ -127,7 +125,7 @@ class FtInTitlePlugin(plugins.BeetsPlugin): remove it from the artist field. """ # In all cases, update the artist fields. - self._log.info(u'artist: {0} -> {1}', item.artist, item.albumartist) + self._log.info('artist: {0} -> {1}', item.artist, item.albumartist) item.artist = item.albumartist if item.artist_sort: # Just strip the featured artist from the sort name. @@ -138,8 +136,8 @@ class FtInTitlePlugin(plugins.BeetsPlugin): if not drop_feat and not contains_feat(item.title): feat_format = self.config['format'].as_str() new_format = feat_format.format(feat_part) - new_title = u"{0} {1}".format(item.title, new_format) - self._log.info(u'title: {0} -> {1}', item.title, new_title) + new_title = f"{item.title} {new_format}" + self._log.info('title: {0} -> {1}', item.title, new_title) item.title = new_title def ft_in_title(self, item, drop_feat): @@ -165,4 +163,4 @@ class FtInTitlePlugin(plugins.BeetsPlugin): if feat_part: self.update_metadata(item, feat_part, drop_feat) else: - self._log.info(u'no featuring artists found') + self._log.info('no featuring artists found') diff --git a/libs/common/beetsplug/fuzzy.py b/libs/common/beetsplug/fuzzy.py index a7308a52..41829639 100644 --- a/libs/common/beetsplug/fuzzy.py +++ b/libs/common/beetsplug/fuzzy.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Philippe Mongeau. # @@ -16,7 +15,6 @@ """Provides a fuzzy matching query. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets.dbcore.query import StringFieldQuery @@ -37,7 +35,7 @@ class FuzzyQuery(StringFieldQuery): class FuzzyPlugin(BeetsPlugin): def __init__(self): - super(FuzzyPlugin, self).__init__() + super().__init__() self.config.add({ 'prefix': '~', 'threshold': 0.7, diff --git a/libs/common/beetsplug/gmusic.py b/libs/common/beetsplug/gmusic.py index 259d2725..844234f9 100644 --- a/libs/common/beetsplug/gmusic.py +++ b/libs/common/beetsplug/gmusic.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- # This file is part of beets. -# Copyright 2017, Tigran Kostandyan. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -13,84 +11,15 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""Upload files to Google Play Music and list songs in its library.""" - -from __future__ import absolute_import, division, print_function -import os.path +"""Deprecation warning for the removed gmusic plugin.""" from beets.plugins import BeetsPlugin -from beets import ui -from beets import config -from beets.ui import Subcommand -from gmusicapi import Musicmanager, Mobileclient -from gmusicapi.exceptions import NotLoggedIn -import gmusicapi.clients class Gmusic(BeetsPlugin): def __init__(self): - super(Gmusic, self).__init__() - # Checks for OAuth2 credentials, - # if they don't exist - performs authorization - self.m = Musicmanager() - if os.path.isfile(gmusicapi.clients.OAUTH_FILEPATH): - self.m.login() - else: - self.m.perform_oauth() + super().__init__() - def commands(self): - gupload = Subcommand('gmusic-upload', - help=u'upload your tracks to Google Play Music') - gupload.func = self.upload - - search = Subcommand('gmusic-songs', - help=u'list of songs in Google Play Music library' - ) - search.parser.add_option('-t', '--track', dest='track', - action='store_true', - help='Search by track name') - search.parser.add_option('-a', '--artist', dest='artist', - action='store_true', - help='Search by artist') - search.func = self.search - return [gupload, search] - - def upload(self, lib, opts, args): - items = lib.items(ui.decargs(args)) - files = [x.path.decode('utf-8') for x in items] - ui.print_(u'Uploading your files...') - self.m.upload(filepaths=files) - ui.print_(u'Your files were successfully added to library') - - def search(self, lib, opts, args): - password = config['gmusic']['password'] - email = config['gmusic']['email'] - password.redact = True - email.redact = True - # Since Musicmanager doesn't support library management - # we need to use mobileclient interface - mobile = Mobileclient() - try: - mobile.login(email.as_str(), password.as_str(), - Mobileclient.FROM_MAC_ADDRESS) - files = mobile.get_all_songs() - except NotLoggedIn: - ui.print_( - u'Authentication error. Please check your email and password.' - ) - return - if not args: - for i, file in enumerate(files, start=1): - print(i, ui.colorize('blue', file['artist']), - file['title'], ui.colorize('red', file['album'])) - else: - if opts.track: - self.match(files, args, 'title') - else: - self.match(files, args, 'artist') - - @staticmethod - def match(files, args, search_by): - for file in files: - if ' '.join(ui.decargs(args)) in file[search_by]: - print(file['artist'], file['title'], file['album']) + self._log.warning("The 'gmusic' plugin has been removed following the" + " shutdown of Google Play Music. Remove the plugin" + " from your configuration to silence this warning.") diff --git a/libs/common/beetsplug/hook.py b/libs/common/beetsplug/hook.py index b6270fd5..0fe3bffc 100644 --- a/libs/common/beetsplug/hook.py +++ b/libs/common/beetsplug/hook.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2015, Adrian Sampson. # @@ -14,14 +13,13 @@ # included in all copies or substantial portions of the Software. """Allows custom commands to be run when an event is emitted by beets""" -from __future__ import division, absolute_import, print_function import string import subprocess -import six +import shlex from beets.plugins import BeetsPlugin -from beets.util import shlex_split, arg_encoding +from beets.util import arg_encoding class CodingFormatter(string.Formatter): @@ -46,13 +44,11 @@ class CodingFormatter(string.Formatter): See str.format and string.Formatter.format. """ - try: + if isinstance(format_string, bytes): format_string = format_string.decode(self._coding) - except UnicodeEncodeError: - pass - return super(CodingFormatter, self).format(format_string, *args, - **kwargs) + return super().format(format_string, *args, + **kwargs) def convert_field(self, value, conversion): """Converts the provided value given a conversion type. @@ -61,8 +57,8 @@ class CodingFormatter(string.Formatter): See string.Formatter.convert_field. """ - converted = super(CodingFormatter, self).convert_field(value, - conversion) + converted = super().convert_field(value, + conversion) if isinstance(converted, bytes): return converted.decode(self._coding) @@ -72,8 +68,9 @@ class CodingFormatter(string.Formatter): class HookPlugin(BeetsPlugin): """Allows custom commands to be run when an event is emitted by beets""" + def __init__(self): - super(HookPlugin, self).__init__() + super().__init__() self.config.add({ 'hooks': [] @@ -91,28 +88,28 @@ class HookPlugin(BeetsPlugin): def create_and_register_hook(self, event, command): def hook_function(**kwargs): - if command is None or len(command) == 0: - self._log.error('invalid command "{0}"', command) - return + if command is None or len(command) == 0: + self._log.error('invalid command "{0}"', command) + return - # Use a string formatter that works on Unicode strings. - if six.PY2: - formatter = CodingFormatter(arg_encoding()) - else: - formatter = string.Formatter() + # Use a string formatter that works on Unicode strings. + formatter = CodingFormatter(arg_encoding()) - command_pieces = shlex_split(command) + command_pieces = shlex.split(command) - for i, piece in enumerate(command_pieces): - command_pieces[i] = formatter.format(piece, event=event, - **kwargs) + for i, piece in enumerate(command_pieces): + command_pieces[i] = formatter.format(piece, event=event, + **kwargs) - self._log.debug(u'running command "{0}" for event {1}', - u' '.join(command_pieces), event) + self._log.debug('running command "{0}" for event {1}', + ' '.join(command_pieces), event) - try: - subprocess.Popen(command_pieces).wait() - except OSError as exc: - self._log.error(u'hook for {0} failed: {1}', event, exc) + try: + subprocess.check_call(command_pieces) + except subprocess.CalledProcessError as exc: + self._log.error('hook for {0} exited with status {1}', + event, exc.returncode) + except OSError as exc: + self._log.error('hook for {0} failed: {1}', event, exc) self.register_listener(event, hook_function) diff --git a/libs/common/beetsplug/ihate.py b/libs/common/beetsplug/ihate.py index 6ed250fe..91850e09 100644 --- a/libs/common/beetsplug/ihate.py +++ b/libs/common/beetsplug/ihate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Blemjhoo Tezoulbr . # @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function """Warns you about things you hate (or even blocks import).""" @@ -33,14 +31,14 @@ def summary(task): object. """ if task.is_album: - return u'{0} - {1}'.format(task.cur_artist, task.cur_album) + return f'{task.cur_artist} - {task.cur_album}' else: - return u'{0} - {1}'.format(task.item.artist, task.item.title) + return f'{task.item.artist} - {task.item.title}' class IHatePlugin(BeetsPlugin): def __init__(self): - super(IHatePlugin, self).__init__() + super().__init__() self.register_listener('import_task_choice', self.import_task_choice_event) self.config.add({ @@ -69,14 +67,14 @@ class IHatePlugin(BeetsPlugin): if task.choice_flag == action.APPLY: if skip_queries or warn_queries: - self._log.debug(u'processing your hate') + self._log.debug('processing your hate') if self.do_i_hate_this(task, skip_queries): task.choice_flag = action.SKIP - self._log.info(u'skipped: {0}', summary(task)) + self._log.info('skipped: {0}', summary(task)) return if self.do_i_hate_this(task, warn_queries): - self._log.info(u'you may hate this: {0}', summary(task)) + self._log.info('you may hate this: {0}', summary(task)) else: - self._log.debug(u'nothing to do') + self._log.debug('nothing to do') else: - self._log.debug(u'user made a decision, nothing to do') + self._log.debug('user made a decision, nothing to do') diff --git a/libs/common/beetsplug/importadded.py b/libs/common/beetsplug/importadded.py index 36407b14..e6665e0f 100644 --- a/libs/common/beetsplug/importadded.py +++ b/libs/common/beetsplug/importadded.py @@ -1,11 +1,8 @@ -# -*- coding: utf-8 -*- - """Populate an item's `added` and `mtime` fields by using the file modification time (mtime) of the item's source file before import. Reimported albums and items are skipped. """ -from __future__ import division, absolute_import, print_function import os @@ -16,7 +13,7 @@ from beets.plugins import BeetsPlugin class ImportAddedPlugin(BeetsPlugin): def __init__(self): - super(ImportAddedPlugin, self).__init__() + super().__init__() self.config.add({ 'preserve_mtimes': False, 'preserve_write_mtimes': False, @@ -27,7 +24,7 @@ class ImportAddedPlugin(BeetsPlugin): # album.path for old albums that were replaced by a reimported album self.replaced_album_paths = None # item path in the library to the mtime of the source file - self.item_mtime = dict() + self.item_mtime = {} register = self.register_listener register('import_task_created', self.check_config) @@ -53,8 +50,8 @@ class ImportAddedPlugin(BeetsPlugin): def record_if_inplace(self, task, session): if not (session.config['copy'] or session.config['move'] or session.config['link'] or session.config['hardlink']): - self._log.debug(u"In place import detected, recording mtimes from " - u"source paths") + self._log.debug("In place import detected, recording mtimes from " + "source paths") items = [task.item] \ if isinstance(task, importer.SingletonImportTask) \ else task.items @@ -62,9 +59,9 @@ class ImportAddedPlugin(BeetsPlugin): self.record_import_mtime(item, item.path, item.path) def record_reimported(self, task, session): - self.reimported_item_ids = set(item.id for item, replaced_items - in task.replaced_items.items() - if replaced_items) + self.reimported_item_ids = {item.id for item, replaced_items + in task.replaced_items.items() + if replaced_items} self.replaced_album_paths = set(task.replaced_albums.keys()) def write_file_mtime(self, path, mtime): @@ -86,14 +83,14 @@ class ImportAddedPlugin(BeetsPlugin): """ mtime = os.stat(util.syspath(source)).st_mtime self.item_mtime[destination] = mtime - self._log.debug(u"Recorded mtime {0} for item '{1}' imported from " - u"'{2}'", mtime, util.displayable_path(destination), + self._log.debug("Recorded mtime {0} for item '{1}' imported from " + "'{2}'", mtime, util.displayable_path(destination), util.displayable_path(source)) def update_album_times(self, lib, album): if self.reimported_album(album): - self._log.debug(u"Album '{0}' is reimported, skipping import of " - u"added dates for the album and its items.", + self._log.debug("Album '{0}' is reimported, skipping import of " + "added dates for the album and its items.", util.displayable_path(album.path)) return @@ -106,30 +103,30 @@ class ImportAddedPlugin(BeetsPlugin): self.write_item_mtime(item, mtime) item.store() album.added = min(album_mtimes) - self._log.debug(u"Import of album '{0}', selected album.added={1} " - u"from item file mtimes.", album.album, album.added) + self._log.debug("Import of album '{0}', selected album.added={1} " + "from item file mtimes.", album.album, album.added) album.store() def update_item_times(self, lib, item): if self.reimported_item(item): - self._log.debug(u"Item '{0}' is reimported, skipping import of " - u"added date.", util.displayable_path(item.path)) + self._log.debug("Item '{0}' is reimported, skipping import of " + "added date.", util.displayable_path(item.path)) return mtime = self.item_mtime.pop(item.path, None) if mtime: item.added = mtime if self.config['preserve_mtimes'].get(bool): self.write_item_mtime(item, mtime) - self._log.debug(u"Import of item '{0}', selected item.added={1}", + self._log.debug("Import of item '{0}', selected item.added={1}", util.displayable_path(item.path), item.added) item.store() - def update_after_write_time(self, item): + def update_after_write_time(self, item, path): """Update the mtime of the item's file with the item.added value after each write of the item if `preserve_write_mtimes` is enabled. """ if item.added: if self.config['preserve_write_mtimes'].get(bool): self.write_item_mtime(item, item.added) - self._log.debug(u"Write of item '{0}', selected item.added={1}", + self._log.debug("Write of item '{0}', selected item.added={1}", util.displayable_path(item.path), item.added) diff --git a/libs/common/beetsplug/importfeeds.py b/libs/common/beetsplug/importfeeds.py index 35ae2883..ad6d8415 100644 --- a/libs/common/beetsplug/importfeeds.py +++ b/libs/common/beetsplug/importfeeds.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Fabrice Laporte. # @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function """Write paths of imported files in various formats to ease later import in a music player. Also allow printing the new file locations to stdout in case @@ -54,11 +52,11 @@ def _write_m3u(m3u_path, items_paths): class ImportFeedsPlugin(BeetsPlugin): def __init__(self): - super(ImportFeedsPlugin, self).__init__() + super().__init__() self.config.add({ 'formats': [], - 'm3u_name': u'imported.m3u', + 'm3u_name': 'imported.m3u', 'dir': None, 'relative_to': None, 'absolute_path': False, @@ -118,9 +116,9 @@ class ImportFeedsPlugin(BeetsPlugin): link(path, dest) if 'echo' in formats: - self._log.info(u"Location of imported music:") + self._log.info("Location of imported music:") for path in paths: - self._log.info(u" {0}", path) + self._log.info(" {0}", path) def album_imported(self, lib, album): self._record_items(lib, album.album, album.items()) diff --git a/libs/common/beetsplug/info.py b/libs/common/beetsplug/info.py index 0d40c597..1e6d4b32 100644 --- a/libs/common/beetsplug/info.py +++ b/libs/common/beetsplug/info.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,19 +15,17 @@ """Shows file metadata. """ -from __future__ import division, absolute_import, print_function import os -import re from beets.plugins import BeetsPlugin from beets import ui -from beets import mediafile +import mediafile from beets.library import Item from beets.util import displayable_path, normpath, syspath -def tag_data(lib, args): +def tag_data(lib, args, album=False): query = [] for arg in args: path = normpath(arg) @@ -42,15 +39,29 @@ def tag_data(lib, args): yield tag_data_emitter(item.path) +def tag_fields(): + fields = set(mediafile.MediaFile.readable_fields()) + fields.add('art') + return fields + + def tag_data_emitter(path): - def emitter(): - fields = list(mediafile.MediaFile.readable_fields()) - fields.remove('images') + def emitter(included_keys): + if included_keys == '*': + fields = tag_fields() + else: + fields = included_keys + if 'images' in fields: + # We can't serialize the image data. + fields.remove('images') mf = mediafile.MediaFile(syspath(path)) tags = {} for field in fields: - tags[field] = getattr(mf, field) - tags['art'] = mf.art is not None + if field == 'art': + tags[field] = mf.art is not None + else: + tags[field] = getattr(mf, field, None) + # create a temporary Item to take advantage of __format__ item = Item.from_path(syspath(path)) @@ -58,15 +69,14 @@ def tag_data_emitter(path): return emitter -def library_data(lib, args): - for item in lib.items(args): +def library_data(lib, args, album=False): + for item in lib.albums(args) if album else lib.items(args): yield library_data_emitter(item) def library_data_emitter(item): - def emitter(): - data = dict(item.formatted()) - data.pop('path', None) # path is fetched from item + def emitter(included_keys): + data = dict(item.formatted(included_keys=included_keys)) return data, item return emitter @@ -98,7 +108,7 @@ def print_data(data, item=None, fmt=None): formatted = {} for key, value in data.items(): if isinstance(value, list): - formatted[key] = u'; '.join(value) + formatted[key] = '; '.join(value) if value is not None: formatted[key] = value @@ -106,7 +116,7 @@ def print_data(data, item=None, fmt=None): return maxwidth = max(len(key) for key in formatted) - lineformat = u'{{0:>{0}}}: {{1}}'.format(maxwidth) + lineformat = f'{{0:>{maxwidth}}}: {{1}}' if path: ui.print_(displayable_path(path)) @@ -114,7 +124,7 @@ def print_data(data, item=None, fmt=None): for field in sorted(formatted): value = formatted[field] if isinstance(value, list): - value = u'; '.join(value) + value = '; '.join(value) ui.print_(lineformat.format(field, value)) @@ -129,7 +139,7 @@ def print_data_keys(data, item=None): if len(formatted) == 0: return - line_format = u'{0}{{0}}'.format(u' ' * 4) + line_format = '{0}{{0}}'.format(' ' * 4) if path: ui.print_(displayable_path(path)) @@ -140,24 +150,28 @@ def print_data_keys(data, item=None): class InfoPlugin(BeetsPlugin): def commands(self): - cmd = ui.Subcommand('info', help=u'show file metadata') + cmd = ui.Subcommand('info', help='show file metadata') cmd.func = self.run cmd.parser.add_option( - u'-l', u'--library', action='store_true', - help=u'show library fields instead of tags', + '-l', '--library', action='store_true', + help='show library fields instead of tags', ) cmd.parser.add_option( - u'-s', u'--summarize', action='store_true', - help=u'summarize the tags of all files', + '-a', '--album', action='store_true', + help='show album fields instead of tracks (implies "--library")', ) cmd.parser.add_option( - u'-i', u'--include-keys', default=[], + '-s', '--summarize', action='store_true', + help='summarize the tags of all files', + ) + cmd.parser.add_option( + '-i', '--include-keys', default=[], action='append', dest='included_keys', - help=u'comma separated list of keys to show', + help='comma separated list of keys to show', ) cmd.parser.add_option( - u'-k', u'--keys-only', action='store_true', - help=u'show only the keys', + '-k', '--keys-only', action='store_true', + help='show only the keys', ) cmd.parser.add_format_option(target='item') return [cmd] @@ -176,7 +190,7 @@ class InfoPlugin(BeetsPlugin): dictionary and only prints that. If two files have different values for the same tag, the value is set to '[various]' """ - if opts.library: + if opts.library or opts.album: data_collector = library_data else: data_collector = tag_data @@ -184,18 +198,21 @@ class InfoPlugin(BeetsPlugin): included_keys = [] for keys in opts.included_keys: included_keys.extend(keys.split(',')) - key_filter = make_key_filter(included_keys) + # Drop path even if user provides it multiple times + included_keys = [k for k in included_keys if k != 'path'] first = True summary = {} - for data_emitter in data_collector(lib, ui.decargs(args)): + for data_emitter in data_collector( + lib, ui.decargs(args), + album=opts.album, + ): try: - data, item = data_emitter() - except (mediafile.UnreadableFileError, IOError) as ex: - self._log.error(u'cannot read file: {0}', ex) + data, item = data_emitter(included_keys or '*') + except (mediafile.UnreadableFileError, OSError) as ex: + self._log.error('cannot read file: {0}', ex) continue - data = key_filter(data) if opts.summarize: update_summary(summary, data) else: @@ -210,33 +227,3 @@ class InfoPlugin(BeetsPlugin): if opts.summarize: print_data(summary) - - -def make_key_filter(include): - """Return a function that filters a dictionary. - - The returned filter takes a dictionary and returns another - dictionary that only includes the key-value pairs where the key - glob-matches one of the keys in `include`. - """ - if not include: - return identity - - matchers = [] - for key in include: - key = re.escape(key) - key = key.replace(r'\*', '.*') - matchers.append(re.compile(key + '$')) - - def filter_(data): - filtered = dict() - for key, value in data.items(): - if any([m.match(key) for m in matchers]): - filtered[key] = value - return filtered - - return filter_ - - -def identity(val): - return val diff --git a/libs/common/beetsplug/inline.py b/libs/common/beetsplug/inline.py index fd0e9fc3..e19eaa9d 100644 --- a/libs/common/beetsplug/inline.py +++ b/libs/common/beetsplug/inline.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -15,25 +14,23 @@ """Allows inline path template customization code in the config file. """ -from __future__ import division, absolute_import, print_function import traceback import itertools from beets.plugins import BeetsPlugin from beets import config -import six -FUNC_NAME = u'__INLINE_FUNC__' +FUNC_NAME = '__INLINE_FUNC__' class InlineError(Exception): """Raised when a runtime error occurs in an inline expression. """ def __init__(self, code, exc): - super(InlineError, self).__init__( - (u"error in inline path field code:\n" - u"%s\n%s: %s") % (code, type(exc).__name__, six.text_type(exc)) + super().__init__( + ("error in inline path field code:\n" + "%s\n%s: %s") % (code, type(exc).__name__, str(exc)) ) @@ -41,7 +38,7 @@ def _compile_func(body): """Given Python code for a function body, return a compiled callable that invokes that code. """ - body = u'def {0}():\n {1}'.format( + body = 'def {}():\n {}'.format( FUNC_NAME, body.replace('\n', '\n ') ) @@ -53,7 +50,7 @@ def _compile_func(body): class InlinePlugin(BeetsPlugin): def __init__(self): - super(InlinePlugin, self).__init__() + super().__init__() config.add({ 'pathfields': {}, # Legacy name. @@ -64,14 +61,14 @@ class InlinePlugin(BeetsPlugin): # Item fields. for key, view in itertools.chain(config['item_fields'].items(), config['pathfields'].items()): - self._log.debug(u'adding item field {0}', key) + self._log.debug('adding item field {0}', key) func = self.compile_inline(view.as_str(), False) if func is not None: self.template_fields[key] = func # Album fields. for key, view in config['album_fields'].items(): - self._log.debug(u'adding album field {0}', key) + self._log.debug('adding album field {0}', key) func = self.compile_inline(view.as_str(), True) if func is not None: self.album_template_fields[key] = func @@ -84,14 +81,14 @@ class InlinePlugin(BeetsPlugin): """ # First, try compiling as a single function. try: - code = compile(u'({0})'.format(python_code), 'inline', 'eval') + code = compile(f'({python_code})', 'inline', 'eval') except SyntaxError: # Fall back to a function body. try: func = _compile_func(python_code) except SyntaxError: - self._log.error(u'syntax error in inline field definition:\n' - u'{0}', traceback.format_exc()) + self._log.error('syntax error in inline field definition:\n' + '{0}', traceback.format_exc()) return else: is_expr = False @@ -117,9 +114,13 @@ class InlinePlugin(BeetsPlugin): # For function bodies, invoke the function with values as global # variables. def _func_func(obj): + old_globals = dict(func.__globals__) func.__globals__.update(_dict_for(obj)) try: return func() except Exception as exc: raise InlineError(python_code, exc) + finally: + func.__globals__.clear() + func.__globals__.update(old_globals) return _func_func diff --git a/libs/common/beetsplug/ipfs.py b/libs/common/beetsplug/ipfs.py index 9a9d6aa5..3c42e7c8 100644 --- a/libs/common/beetsplug/ipfs.py +++ b/libs/common/beetsplug/ipfs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # # Permission is hereby granted, free of charge, to any person obtaining @@ -15,7 +14,6 @@ """Adds support for ipfs. Requires go-ipfs and a running ipfs daemon """ -from __future__ import division, absolute_import, print_function from beets import ui, util, library, config from beets.plugins import BeetsPlugin @@ -29,9 +27,10 @@ import tempfile class IPFSPlugin(BeetsPlugin): def __init__(self): - super(IPFSPlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, + 'nocopy': False, }) if self.config['auto']: @@ -116,12 +115,15 @@ class IPFSPlugin(BeetsPlugin): self._log.info('Adding {0} to ipfs', album_dir) - cmd = "ipfs add -q -r".split() + if self.config['nocopy']: + cmd = "ipfs add --nocopy -q -r".split() + else: + cmd = "ipfs add -q -r".split() cmd.append(album_dir) try: - output = util.command_output(cmd).split() + output = util.command_output(cmd).stdout.split() except (OSError, subprocess.CalledProcessError) as exc: - self._log.error(u'Failed to add {0}, error: {1}', album_dir, exc) + self._log.error('Failed to add {0}, error: {1}', album_dir, exc) return False length = len(output) @@ -147,6 +149,8 @@ class IPFSPlugin(BeetsPlugin): def ipfs_get(self, lib, query): query = query[0] # Check if query is a hash + # TODO: generalize to other hashes; probably use a multihash + # implementation if query.startswith("Qm") and len(query) == 46: self.ipfs_get_from_hash(lib, query) else: @@ -174,11 +178,14 @@ class IPFSPlugin(BeetsPlugin): with tempfile.NamedTemporaryFile() as tmp: self.ipfs_added_albums(lib, tmp.name) try: - cmd = "ipfs add -q ".split() + if self.config['nocopy']: + cmd = "ipfs add --nocopy -q ".split() + else: + cmd = "ipfs add -q ".split() cmd.append(tmp.name) - output = util.command_output(cmd) + output = util.command_output(cmd).stdout except (OSError, subprocess.CalledProcessError) as err: - msg = "Failed to publish library. Error: {0}".format(err) + msg = f"Failed to publish library. Error: {err}" self._log.error(msg) return False self._log.info("hash of library: {0}", output) @@ -190,26 +197,26 @@ class IPFSPlugin(BeetsPlugin): else: lib_name = _hash lib_root = os.path.dirname(lib.path) - remote_libs = lib_root + "/remotes" + remote_libs = os.path.join(lib_root, b"remotes") if not os.path.exists(remote_libs): try: os.makedirs(remote_libs) except OSError as e: - msg = "Could not create {0}. Error: {1}".format(remote_libs, e) + msg = f"Could not create {remote_libs}. Error: {e}" self._log.error(msg) return False - path = remote_libs + "/" + lib_name + ".db" + path = os.path.join(remote_libs, lib_name.encode() + b".db") if not os.path.exists(path): - cmd = "ipfs get {0} -o".format(_hash).split() + cmd = f"ipfs get {_hash} -o".split() cmd.append(path) try: util.command_output(cmd) except (OSError, subprocess.CalledProcessError): - self._log.error("Could not import {0}".format(_hash)) + self._log.error(f"Could not import {_hash}") return False # add all albums from remotes into a combined library - jpath = remote_libs + "/joined.db" + jpath = os.path.join(remote_libs, b"joined.db") jlib = library.Library(jpath) nlib = library.Library(path) for album in nlib.albums(): @@ -232,12 +239,12 @@ class IPFSPlugin(BeetsPlugin): fmt = config['format_album'].get() try: albums = self.query(lib, args) - except IOError: + except OSError: ui.print_("No imported libraries yet.") return for album in albums: - ui.print_(format(album, fmt), " : ", album.ipfs) + ui.print_(format(album, fmt), " : ", album.ipfs.decode()) def query(self, lib, args): rlib = self.get_remote_lib(lib) @@ -246,10 +253,10 @@ class IPFSPlugin(BeetsPlugin): def get_remote_lib(self, lib): lib_root = os.path.dirname(lib.path) - remote_libs = lib_root + "/remotes" - path = remote_libs + "/joined.db" + remote_libs = os.path.join(lib_root, b"remotes") + path = os.path.join(remote_libs, b"joined.db") if not os.path.isfile(path): - raise IOError + raise OSError return library.Library(path) def ipfs_added_albums(self, rlib, tmpname): @@ -276,7 +283,7 @@ class IPFSPlugin(BeetsPlugin): util._fsencoding(), 'ignore' ) # Clear current path from item - item.path = '/ipfs/{0}/{1}'.format(album.ipfs, item_path) + item.path = f'/ipfs/{album.ipfs}/{item_path}' item.id = None items.append(item) diff --git a/libs/common/beetsplug/keyfinder.py b/libs/common/beetsplug/keyfinder.py index a3fbc821..b695ab54 100644 --- a/libs/common/beetsplug/keyfinder.py +++ b/libs/common/beetsplug/keyfinder.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Thomas Scholtes. # @@ -16,8 +15,8 @@ """Uses the `KeyFinder` program to add the `initial_key` field. """ -from __future__ import division, absolute_import, print_function +import os.path import subprocess from beets import ui @@ -28,11 +27,11 @@ from beets.plugins import BeetsPlugin class KeyFinderPlugin(BeetsPlugin): def __init__(self): - super(KeyFinderPlugin, self).__init__() + super().__init__() self.config.add({ - u'bin': u'KeyFinder', - u'auto': True, - u'overwrite': False, + 'bin': 'KeyFinder', + 'auto': True, + 'overwrite': False, }) if self.config['auto'].get(bool): @@ -40,7 +39,7 @@ class KeyFinderPlugin(BeetsPlugin): def commands(self): cmd = ui.Subcommand('keyfinder', - help=u'detect and add initial key from audio') + help='detect and add initial key from audio') cmd.func = self.command return [cmd] @@ -52,34 +51,45 @@ class KeyFinderPlugin(BeetsPlugin): def find_key(self, items, write=False): overwrite = self.config['overwrite'].get(bool) - bin = self.config['bin'].as_str() + command = [self.config['bin'].as_str()] + # The KeyFinder GUI program needs the -f flag before the path. + # keyfinder-cli is similar, but just wants the path with no flag. + if 'keyfinder-cli' not in os.path.basename(command[0]).lower(): + command.append('-f') for item in items: if item['initial_key'] and not overwrite: continue try: - output = util.command_output([bin, '-f', - util.syspath(item.path)]) + output = util.command_output(command + [util.syspath( + item.path)]).stdout except (subprocess.CalledProcessError, OSError) as exc: - self._log.error(u'execution failed: {0}', exc) + self._log.error('execution failed: {0}', exc) continue except UnicodeEncodeError: # Workaround for Python 2 Windows bug. - # http://bugs.python.org/issue1759845 - self._log.error(u'execution failed for Unicode path: {0!r}', + # https://bugs.python.org/issue1759845 + self._log.error('execution failed for Unicode path: {0!r}', item.path) continue - key_raw = output.rsplit(None, 1)[-1] + try: + key_raw = output.rsplit(None, 1)[-1] + except IndexError: + # Sometimes keyfinder-cli returns 0 but with no key, usually + # when the file is silent or corrupt, so we log and skip. + self._log.error('no key returned for path: {0}', item.path) + continue + try: key = util.text_string(key_raw) except UnicodeDecodeError: - self._log.error(u'output is invalid UTF-8') + self._log.error('output is invalid UTF-8') continue item['initial_key'] = key - self._log.info(u'added computed initial key {0} for {1}', + self._log.info('added computed initial key {0} for {1}', key, util.displayable_path(item.path)) if write: diff --git a/libs/common/beetsplug/kodiupdate.py b/libs/common/beetsplug/kodiupdate.py index ce5cb478..2a885d2c 100644 --- a/libs/common/beetsplug/kodiupdate.py +++ b/libs/common/beetsplug/kodiupdate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2017, Pauli Kettunen. # @@ -23,18 +22,16 @@ Put something like the following in your config.yaml to configure: user: user pwd: secret """ -from __future__ import division, absolute_import, print_function import requests from beets import config from beets.plugins import BeetsPlugin -import six def update_kodi(host, port, user, password): """Sends request to the Kodi api to start a library refresh. """ - url = "http://{0}:{1}/jsonrpc".format(host, port) + url = f"http://{host}:{port}/jsonrpc" """Content-Type: application/json is mandatory according to the kodi jsonrpc documentation""" @@ -54,14 +51,14 @@ def update_kodi(host, port, user, password): class KodiUpdate(BeetsPlugin): def __init__(self): - super(KodiUpdate, self).__init__() + super().__init__() # Adding defaults. config['kodi'].add({ - u'host': u'localhost', - u'port': 8080, - u'user': u'kodi', - u'pwd': u'kodi'}) + 'host': 'localhost', + 'port': 8080, + 'user': 'kodi', + 'pwd': 'kodi'}) config['kodi']['pwd'].redact = True self.register_listener('database_change', self.listen_for_db_change) @@ -73,7 +70,7 @@ class KodiUpdate(BeetsPlugin): def update(self, lib): """When the client exists try to send refresh request to Kodi server. """ - self._log.info(u'Requesting a Kodi library update...') + self._log.info('Requesting a Kodi library update...') # Try to send update request. try: @@ -85,14 +82,14 @@ class KodiUpdate(BeetsPlugin): r.raise_for_status() except requests.exceptions.RequestException as e: - self._log.warning(u'Kodi update failed: {0}', - six.text_type(e)) + self._log.warning('Kodi update failed: {0}', + str(e)) return json = r.json() if json.get('result') != 'OK': - self._log.warning(u'Kodi update failed: JSON response was {0!r}', + self._log.warning('Kodi update failed: JSON response was {0!r}', json) return - self._log.info(u'Kodi update triggered') + self._log.info('Kodi update triggered') diff --git a/libs/common/beetsplug/lastgenre/__init__.py b/libs/common/beetsplug/lastgenre/__init__.py index 4374310b..05412308 100644 --- a/libs/common/beetsplug/lastgenre/__init__.py +++ b/libs/common/beetsplug/lastgenre/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -13,8 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function -import six """Gets genres for imported music based on Last.fm tags. @@ -46,7 +43,7 @@ PYLAST_EXCEPTIONS = ( ) REPLACE = { - u'\u2010': '-', + '\u2010': '-', } @@ -73,7 +70,7 @@ def flatten_tree(elem, path, branches): for sub in elem: flatten_tree(sub, path, branches) else: - branches.append(path + [six.text_type(elem)]) + branches.append(path + [str(elem)]) def find_parents(candidate, branches): @@ -97,7 +94,7 @@ C14N_TREE = os.path.join(os.path.dirname(__file__), 'genres-tree.yaml') class LastGenrePlugin(plugins.BeetsPlugin): def __init__(self): - super(LastGenrePlugin, self).__init__() + super().__init__() self.config.add({ 'whitelist': True, @@ -108,8 +105,9 @@ class LastGenrePlugin(plugins.BeetsPlugin): 'source': 'album', 'force': True, 'auto': True, - 'separator': u', ', + 'separator': ', ', 'prefer_specific': False, + 'title_case': True, }) self.setup() @@ -132,18 +130,27 @@ class LastGenrePlugin(plugins.BeetsPlugin): with open(wl_filename, 'rb') as f: for line in f: line = line.decode('utf-8').strip().lower() - if line and not line.startswith(u'#'): + if line and not line.startswith('#'): self.whitelist.add(line) # Read the genres tree for canonicalization if enabled. self.c14n_branches = [] c14n_filename = self.config['canonical'].get() - if c14n_filename in (True, ''): # Default tree. + self.canonicalize = c14n_filename is not False + + # Default tree + if c14n_filename in (True, ''): c14n_filename = C14N_TREE + elif not self.canonicalize and self.config['prefer_specific'].get(): + # prefer_specific requires a tree, load default tree + c14n_filename = C14N_TREE + + # Read the tree if c14n_filename: + self._log.debug('Loading canonicalization tree {0}', c14n_filename) c14n_filename = normpath(c14n_filename) with codecs.open(c14n_filename, 'r', encoding='utf-8') as f: - genres_tree = yaml.load(f) + genres_tree = yaml.safe_load(f) flatten_tree(genres_tree, [], self.c14n_branches) @property @@ -186,7 +193,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): return None count = self.config['count'].get(int) - if self.c14n_branches: + if self.canonicalize: # Extend the list to consider tags parents in the c14n tree tags_all = [] for tag in tags: @@ -214,12 +221,17 @@ class LastGenrePlugin(plugins.BeetsPlugin): # c14n only adds allowed genres but we may have had forbidden genres in # the original tags list - tags = [x.title() for x in tags if self._is_allowed(x)] + tags = [self._format_tag(x) for x in tags if self._is_allowed(x)] return self.config['separator'].as_str().join( tags[:self.config['count'].get(int)] ) + def _format_tag(self, tag): + if self.config["title_case"]: + return tag.title() + return tag + def fetch_genre(self, lastfm_obj): """Return the genre for a pylast entity or None if no suitable genre can be found. Ex. 'Electronic, House, Dance' @@ -251,8 +263,8 @@ class LastGenrePlugin(plugins.BeetsPlugin): if any(not s for s in args): return None - key = u'{0}.{1}'.format(entity, - u'-'.join(six.text_type(a) for a in args)) + key = '{}.{}'.format(entity, + '-'.join(str(a) for a in args)) if key in self._genre_cache: return self._genre_cache[key] else: @@ -270,28 +282,28 @@ class LastGenrePlugin(plugins.BeetsPlugin): """Return the album genre for this Item or Album. """ return self._last_lookup( - u'album', LASTFM.get_album, obj.albumartist, obj.album + 'album', LASTFM.get_album, obj.albumartist, obj.album ) def fetch_album_artist_genre(self, obj): """Return the album artist genre for this Item or Album. """ return self._last_lookup( - u'artist', LASTFM.get_artist, obj.albumartist + 'artist', LASTFM.get_artist, obj.albumartist ) def fetch_artist_genre(self, item): """Returns the track artist genre for this Item. """ return self._last_lookup( - u'artist', LASTFM.get_artist, item.artist + 'artist', LASTFM.get_artist, item.artist ) def fetch_track_genre(self, obj): """Returns the track genre for this Item. """ return self._last_lookup( - u'track', LASTFM.get_track, obj.artist, obj.title + 'track', LASTFM.get_track, obj.artist, obj.title ) def _get_genre(self, obj): @@ -361,38 +373,56 @@ class LastGenrePlugin(plugins.BeetsPlugin): return None, None def commands(self): - lastgenre_cmd = ui.Subcommand('lastgenre', help=u'fetch genres') + lastgenre_cmd = ui.Subcommand('lastgenre', help='fetch genres') lastgenre_cmd.parser.add_option( - u'-f', u'--force', dest='force', - action='store_true', default=False, - help=u're-download genre when already present' + '-f', '--force', dest='force', + action='store_true', + help='re-download genre when already present' ) lastgenre_cmd.parser.add_option( - u'-s', u'--source', dest='source', type='string', - help=u'genre source: artist, album, or track' + '-s', '--source', dest='source', type='string', + help='genre source: artist, album, or track' ) + lastgenre_cmd.parser.add_option( + '-A', '--items', action='store_false', dest='album', + help='match items instead of albums') + lastgenre_cmd.parser.add_option( + '-a', '--albums', action='store_true', dest='album', + help='match albums instead of items') + lastgenre_cmd.parser.set_defaults(album=True) def lastgenre_func(lib, opts, args): write = ui.should_write() self.config.set_args(opts) - for album in lib.albums(ui.decargs(args)): - album.genre, src = self._get_genre(album) - self._log.info(u'genre for album {0} ({1}): {0.genre}', - album, src) - album.store() + if opts.album: + # Fetch genres for whole albums + for album in lib.albums(ui.decargs(args)): + album.genre, src = self._get_genre(album) + self._log.info('genre for album {0} ({1}): {0.genre}', + album, src) + album.store() - for item in album.items(): - # If we're using track-level sources, also look up each - # track on the album. - if 'track' in self.sources: - item.genre, src = self._get_genre(item) - item.store() - self._log.info(u'genre for track {0} ({1}): {0.genre}', - item, src) + for item in album.items(): + # If we're using track-level sources, also look up each + # track on the album. + if 'track' in self.sources: + item.genre, src = self._get_genre(item) + item.store() + self._log.info( + 'genre for track {0} ({1}): {0.genre}', + item, src) - if write: - item.try_write() + if write: + item.try_write() + else: + # Just query singletons, i.e. items that are not part of + # an album + for item in lib.items(ui.decargs(args)): + item.genre, src = self._get_genre(item) + self._log.debug('added last.fm item genre ({0}): {1}', + src, item.genre) + item.store() lastgenre_cmd.func = lastgenre_func return [lastgenre_cmd] @@ -402,21 +432,21 @@ class LastGenrePlugin(plugins.BeetsPlugin): if task.is_album: album = task.album album.genre, src = self._get_genre(album) - self._log.debug(u'added last.fm album genre ({0}): {1}', + self._log.debug('added last.fm album genre ({0}): {1}', src, album.genre) album.store() if 'track' in self.sources: for item in album.items(): item.genre, src = self._get_genre(item) - self._log.debug(u'added last.fm item genre ({0}): {1}', + self._log.debug('added last.fm item genre ({0}): {1}', src, item.genre) item.store() else: item = task.item item.genre, src = self._get_genre(item) - self._log.debug(u'added last.fm item genre ({0}): {1}', + self._log.debug('added last.fm item genre ({0}): {1}', src, item.genre) item.store() @@ -438,12 +468,12 @@ class LastGenrePlugin(plugins.BeetsPlugin): try: res = obj.get_top_tags() except PYLAST_EXCEPTIONS as exc: - self._log.debug(u'last.fm error: {0}', exc) + self._log.debug('last.fm error: {0}', exc) return [] except Exception as exc: # Isolate bugs in pylast. - self._log.debug(u'{}', traceback.format_exc()) - self._log.error(u'error in pylast library: {0}', exc) + self._log.debug('{}', traceback.format_exc()) + self._log.error('error in pylast library: {0}', exc) return [] # Filter by weight (optionally). diff --git a/libs/common/beetsplug/lastgenre/genres-tree.yaml b/libs/common/beetsplug/lastgenre/genres-tree.yaml index a09f7e6b..c8ae4247 100644 --- a/libs/common/beetsplug/lastgenre/genres-tree.yaml +++ b/libs/common/beetsplug/lastgenre/genres-tree.yaml @@ -648,35 +648,51 @@ - glam rock - hard rock - heavy metal: - - alternative metal + - alternative metal: + - funk metal - black metal: - viking metal - christian metal - death metal: + - death/doom - goregrind - melodic death metal - technical death metal - - doom metal + - doom metal: + - epic doom metal + - funeral doom - drone metal + - epic metal - folk metal: - celtic metal - medieval metal + - pagan metal - funk metal - glam metal - gothic metal + - industrial metal: + - industrial death metal - metalcore: - deathcore - mathcore: - djent - - power metal + - synthcore + - neoclassical metal + - post-metal + - power metal: + - progressive power metal - progressive metal - sludge metal - speed metal - - stoner rock + - stoner rock: + - stoner metal - symphonic metal - thrash metal: - crossover thrash - groove metal + - progressive thrash metal + - teutonic thrash metal + - traditional heavy metal - math rock - new wave: - world fusion @@ -719,6 +735,7 @@ - street punk - thrashcore - horror punk + - oi! - pop punk - psychobilly - riot grrrl diff --git a/libs/common/beetsplug/lastgenre/genres.txt b/libs/common/beetsplug/lastgenre/genres.txt index 914ee129..7ccd7ad3 100644 --- a/libs/common/beetsplug/lastgenre/genres.txt +++ b/libs/common/beetsplug/lastgenre/genres.txt @@ -450,6 +450,8 @@ emo rap emocore emotronic enka +epic doom metal +epic metal eremwu eu ethereal pop ethereal wave @@ -1024,6 +1026,7 @@ neo-medieval neo-prog neo-psychedelia neoclassical +neoclassical metal neoclassical music neofolk neotraditional country @@ -1176,8 +1179,10 @@ progressive folk progressive folk music progressive house progressive metal +progressive power metal progressive rock progressive trance +progressive thrash metal protopunk psych folk psychedelic music @@ -1396,6 +1401,7 @@ symphonic metal symphonic poem symphonic rock symphony +synthcore synthpop synthpunk t'ong guitar @@ -1428,6 +1434,7 @@ tejano tejano music tekno tembang sunda +teutonic thrash metal texas blues thai pop thillana @@ -1444,6 +1451,7 @@ toeshey togaku trad jazz traditional bluegrass +traditional heavy metal traditional pop music trallalero trance diff --git a/libs/common/beetsplug/lastimport.py b/libs/common/beetsplug/lastimport.py index d7b84b0a..16d53302 100644 --- a/libs/common/beetsplug/lastimport.py +++ b/libs/common/beetsplug/lastimport.py @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- # This file is part of beets. -# Copyright 2016, Rafael Bodill http://github.com/rafi +# Copyright 2016, Rafael Bodill https://github.com/rafi # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function import pylast from pylast import TopItem, _extract, _number @@ -28,7 +26,7 @@ API_URL = 'https://ws.audioscrobbler.com/2.0/' class LastImportPlugin(plugins.BeetsPlugin): def __init__(self): - super(LastImportPlugin, self).__init__() + super().__init__() config['lastfm'].add({ 'user': '', 'api_key': plugins.LASTFM_KEY, @@ -43,7 +41,7 @@ class LastImportPlugin(plugins.BeetsPlugin): } def commands(self): - cmd = ui.Subcommand('lastimport', help=u'import last.fm play-count') + cmd = ui.Subcommand('lastimport', help='import last.fm play-count') def func(lib, opts, args): import_lastfm(lib, self._log) @@ -59,7 +57,7 @@ class CustomUser(pylast.User): tracks. """ def __init__(self, *args, **kwargs): - super(CustomUser, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _get_things(self, method, thing, thing_type, params=None, cacheable=True): @@ -114,9 +112,9 @@ def import_lastfm(lib, log): per_page = config['lastimport']['per_page'].get(int) if not user: - raise ui.UserError(u'You must specify a user name for lastimport') + raise ui.UserError('You must specify a user name for lastimport') - log.info(u'Fetching last.fm library for @{0}', user) + log.info('Fetching last.fm library for @{0}', user) page_total = 1 page_current = 0 @@ -125,15 +123,15 @@ def import_lastfm(lib, log): retry_limit = config['lastimport']['retry_limit'].get(int) # Iterate through a yet to be known page total count while page_current < page_total: - log.info(u'Querying page #{0}{1}...', + log.info('Querying page #{0}{1}...', page_current + 1, - '/{}'.format(page_total) if page_total > 1 else '') + f'/{page_total}' if page_total > 1 else '') for retry in range(0, retry_limit): tracks, page_total = fetch_tracks(user, page_current + 1, per_page) if page_total < 1: # It means nothing to us! - raise ui.UserError(u'Last.fm reported no data.') + raise ui.UserError('Last.fm reported no data.') if tracks: found, unknown = process_tracks(lib, tracks, log) @@ -141,22 +139,22 @@ def import_lastfm(lib, log): unknown_total += unknown break else: - log.error(u'ERROR: unable to read page #{0}', + log.error('ERROR: unable to read page #{0}', page_current + 1) if retry < retry_limit: log.info( - u'Retrying page #{0}... ({1}/{2} retry)', + 'Retrying page #{0}... ({1}/{2} retry)', page_current + 1, retry + 1, retry_limit ) else: - log.error(u'FAIL: unable to fetch page #{0}, ', - u'tried {1} times', page_current, retry + 1) + log.error('FAIL: unable to fetch page #{0}, ', + 'tried {1} times', page_current, retry + 1) page_current += 1 - log.info(u'... done!') - log.info(u'finished processing {0} song pages', page_total) - log.info(u'{0} unknown play-counts', unknown_total) - log.info(u'{0} play-counts imported', found_total) + log.info('... done!') + log.info('finished processing {0} song pages', page_total) + log.info('{0} unknown play-counts', unknown_total) + log.info('{0} play-counts imported', found_total) def fetch_tracks(user, page, limit): @@ -190,7 +188,7 @@ def process_tracks(lib, tracks, log): total = len(tracks) total_found = 0 total_fails = 0 - log.info(u'Received {0} tracks in this page, processing...', total) + log.info('Received {0} tracks in this page, processing...', total) for num in range(0, total): song = None @@ -201,7 +199,7 @@ def process_tracks(lib, tracks, log): if 'album' in tracks[num]: album = tracks[num]['album'].get('name', '').strip() - log.debug(u'query: {0} - {1} ({2})', artist, title, album) + log.debug('query: {0} - {1} ({2})', artist, title, album) # First try to query by musicbrainz's trackid if trackid: @@ -211,7 +209,7 @@ def process_tracks(lib, tracks, log): # If not, try just artist/title if song is None: - log.debug(u'no album match, trying by artist/title') + log.debug('no album match, trying by artist/title') query = dbcore.AndQuery([ dbcore.query.SubstringQuery('artist', artist), dbcore.query.SubstringQuery('title', title) @@ -220,8 +218,8 @@ def process_tracks(lib, tracks, log): # Last resort, try just replacing to utf-8 quote if song is None: - title = title.replace("'", u'\u2019') - log.debug(u'no title match, trying utf-8 single quote') + title = title.replace("'", '\u2019') + log.debug('no title match, trying utf-8 single quote') query = dbcore.AndQuery([ dbcore.query.SubstringQuery('artist', artist), dbcore.query.SubstringQuery('title', title) @@ -231,19 +229,19 @@ def process_tracks(lib, tracks, log): if song is not None: count = int(song.get('play_count', 0)) new_count = int(tracks[num]['playcount']) - log.debug(u'match: {0} - {1} ({2}) ' - u'updating: play_count {3} => {4}', + log.debug('match: {0} - {1} ({2}) ' + 'updating: play_count {3} => {4}', song.artist, song.title, song.album, count, new_count) song['play_count'] = new_count song.store() total_found += 1 else: total_fails += 1 - log.info(u' - No match: {0} - {1} ({2})', + log.info(' - No match: {0} - {1} ({2})', artist, title, album) if total_fails > 0: - log.info(u'Acquired {0}/{1} play-counts ({2} unknown)', + log.info('Acquired {0}/{1} play-counts ({2} unknown)', total_found, total, total_fails) return total_found, total_fails diff --git a/libs/common/beetsplug/loadext.py b/libs/common/beetsplug/loadext.py new file mode 100644 index 00000000..191b97a2 --- /dev/null +++ b/libs/common/beetsplug/loadext.py @@ -0,0 +1,44 @@ +# This file is part of beets. +# Copyright 2019, Jack Wilsdon +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Load SQLite extensions. +""" + + +from beets.dbcore import Database +from beets.plugins import BeetsPlugin +import sqlite3 + + +class LoadExtPlugin(BeetsPlugin): + def __init__(self): + super().__init__() + + if not Database.supports_extensions: + self._log.warn('loadext is enabled but the current SQLite ' + 'installation does not support extensions') + return + + self.register_listener('library_opened', self.library_opened) + + def library_opened(self, lib): + for v in self.config: + ext = v.as_filename() + + self._log.debug('loading extension {}', ext) + + try: + lib.load_extension(ext) + except sqlite3.OperationalError as e: + self._log.error('failed to load extension {}: {}', ext, e) diff --git a/libs/common/beetsplug/lyrics.py b/libs/common/beetsplug/lyrics.py index 60f53759..2cb50ca5 100644 --- a/libs/common/beetsplug/lyrics.py +++ b/libs/common/beetsplug/lyrics.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,7 +15,6 @@ """Fetches, embeds, and displays lyrics. """ -from __future__ import absolute_import, division, print_function import difflib import errno @@ -29,11 +27,11 @@ import requests import unicodedata from unidecode import unidecode import warnings -import six -from six.moves import urllib +import urllib try: - from bs4 import SoupStrainer, BeautifulSoup + import bs4 + from bs4 import SoupStrainer HAS_BEAUTIFUL_SOUP = True except ImportError: HAS_BEAUTIFUL_SOUP = False @@ -48,7 +46,7 @@ try: # PY3: HTMLParseError was removed in 3.5 as strict mode # was deprecated in 3.3. # https://docs.python.org/3.3/library/html.parser.html - from six.moves.html_parser import HTMLParseError + from html.parser import HTMLParseError except ImportError: class HTMLParseError(Exception): pass @@ -62,23 +60,23 @@ COMMENT_RE = re.compile(r'', re.S) TAG_RE = re.compile(r'<[^>]*>') BREAK_RE = re.compile(r'\n?\s*]*)*>\s*\n?', re.I) URL_CHARACTERS = { - u'\u2018': u"'", - u'\u2019': u"'", - u'\u201c': u'"', - u'\u201d': u'"', - u'\u2010': u'-', - u'\u2011': u'-', - u'\u2012': u'-', - u'\u2013': u'-', - u'\u2014': u'-', - u'\u2015': u'-', - u'\u2016': u'-', - u'\u2026': u'...', + '\u2018': "'", + '\u2019': "'", + '\u201c': '"', + '\u201d': '"', + '\u2010': '-', + '\u2011': '-', + '\u2012': '-', + '\u2013': '-', + '\u2014': '-', + '\u2015': '-', + '\u2016': '-', + '\u2026': '...', } -USER_AGENT = 'beets/{}'.format(beets.__version__) +USER_AGENT = f'beets/{beets.__version__}' # The content for the base index.rst generated in ReST mode. -REST_INDEX_TEMPLATE = u'''Lyrics +REST_INDEX_TEMPLATE = '''Lyrics ====== * :ref:`Song index ` @@ -94,11 +92,11 @@ Artist index: ''' # The content for the base conf.py generated. -REST_CONF_TEMPLATE = u'''# -*- coding: utf-8 -*- +REST_CONF_TEMPLATE = '''# -*- coding: utf-8 -*- master_doc = 'index' -project = u'Lyrics' -copyright = u'none' -author = u'Various Authors' +project = 'Lyrics' +copyright = 'none' +author = 'Various Authors' latex_documents = [ (master_doc, 'Lyrics.tex', project, author, 'manual'), @@ -117,7 +115,7 @@ epub_tocdup = False def unichar(i): try: - return six.unichr(i) + return chr(i) except ValueError: return struct.pack('i', i).decode('utf-32') @@ -126,12 +124,12 @@ def unescape(text): """Resolve &#xxx; HTML entities (and some others).""" if isinstance(text, bytes): text = text.decode('utf-8', 'ignore') - out = text.replace(u' ', u' ') + out = text.replace(' ', ' ') def replchar(m): num = m.group(1) return unichar(int(num)) - out = re.sub(u"&#(\d+);", replchar, out) + out = re.sub("&#(\\d+);", replchar, out) return out @@ -140,43 +138,10 @@ def extract_text_between(html, start_marker, end_marker): _, html = html.split(start_marker, 1) html, _ = html.split(end_marker, 1) except ValueError: - return u'' + return '' return html -def extract_text_in(html, starttag): - """Extract the text from a
    tag in the HTML starting with - ``starttag``. Returns None if parsing fails. - """ - # Strip off the leading text before opening tag. - try: - _, html = html.split(starttag, 1) - except ValueError: - return - - # Walk through balanced DIV tags. - level = 0 - parts = [] - pos = 0 - for match in DIV_RE.finditer(html): - if match.group(1): # Closing tag. - level -= 1 - if level == 0: - pos = match.end() - else: # Opening tag. - if level == 0: - parts.append(html[pos:match.start()]) - level += 1 - - if level == -1: - parts.append(html[pos:match.start()]) - break - else: - print(u'no closing tag found!') - return - return u''.join(parts) - - def search_pairs(item): """Yield a pairs of artists and titles to search for. @@ -186,6 +151,9 @@ def search_pairs(item): In addition to the artist and title obtained from the `item` the method tries to strip extra information like paranthesized suffixes and featured artists from the strings and add them as candidates. + The artist sort name is added as a fallback candidate to help in + cases where artist name includes special characters or is in a + non-latin script. The method also tries to split multiple titles separated with `/`. """ def generate_alternatives(string, patterns): @@ -199,19 +167,23 @@ def search_pairs(item): alternatives.append(match.group(1)) return alternatives - title, artist = item.title, item.artist + title, artist, artist_sort = item.title, item.artist, item.artist_sort patterns = [ # Remove any featuring artists from the artists name - r"(.*?) {0}".format(plugins.feat_tokens())] + fr"(.*?) {plugins.feat_tokens()}"] artists = generate_alternatives(artist, patterns) + # Use the artist_sort as fallback only if it differs from artist to avoid + # repeated remote requests with the same search terms + if artist != artist_sort: + artists.append(artist_sort) patterns = [ # Remove a parenthesized suffix from a title string. Common # examples include (live), (remix), and (acoustic). r"(.+?)\s+[(].*[)]$", # Remove any featuring artists from the title - r"(.*?) {0}".format(plugins.feat_tokens(for_artist=False)), + r"(.*?) {}".format(plugins.feat_tokens(for_artist=False)), # Remove part of title after colon ':' for songs with subtitles r"(.+?)\s*:.*"] titles = generate_alternatives(title, patterns) @@ -245,14 +217,27 @@ def slug(text): return re.sub(r'\W+', '-', unidecode(text).lower().strip()).strip('-') -class Backend(object): +if HAS_BEAUTIFUL_SOUP: + def try_parse_html(html, **kwargs): + try: + return bs4.BeautifulSoup(html, 'html.parser', **kwargs) + except HTMLParseError: + return None +else: + def try_parse_html(html, **kwargs): + return None + + +class Backend: + REQUIRES_BS = False + def __init__(self, config, log): self._log = log @staticmethod def _encode(s): """Encode the string for inclusion in a URL""" - if isinstance(s, six.text_type): + if isinstance(s, str): for char, repl in URL_CHARACTERS.items(): s = s.replace(char, repl) s = s.encode('utf-8', 'ignore') @@ -277,20 +262,21 @@ class Backend(object): 'User-Agent': USER_AGENT, }) except requests.RequestException as exc: - self._log.debug(u'lyrics request failed: {0}', exc) + self._log.debug('lyrics request failed: {0}', exc) return if r.status_code == requests.codes.ok: return r.text else: - self._log.debug(u'failed to fetch: {0} ({1})', url, r.status_code) + self._log.debug('failed to fetch: {0} ({1})', url, r.status_code) + return None def fetch(self, artist, title): raise NotImplementedError() -class SymbolsReplaced(Backend): +class MusiXmatch(Backend): REPLACEMENTS = { - r'\s+': '_', + r'\s+': '-', '<': 'Less_Than', '>': 'Greater_Than', '#': 'Number_', @@ -298,39 +284,40 @@ class SymbolsReplaced(Backend): r'[\]\}]': ')', } + URL_PATTERN = 'https://www.musixmatch.com/lyrics/%s/%s' + @classmethod def _encode(cls, s): for old, new in cls.REPLACEMENTS.items(): s = re.sub(old, new, s) - return super(SymbolsReplaced, cls)._encode(s) - - -class MusiXmatch(SymbolsReplaced): - REPLACEMENTS = dict(SymbolsReplaced.REPLACEMENTS, **{ - r'\s+': '-' - }) - - URL_PATTERN = 'https://www.musixmatch.com/lyrics/%s/%s' + return super()._encode(s) def fetch(self, artist, title): url = self.build_url(artist, title) html = self.fetch_url(url) if not html: - return + return None if "We detected that your IP is blocked" in html: - self._log.warning(u'we are blocked at MusixMatch: url %s failed' + self._log.warning('we are blocked at MusixMatch: url %s failed' % url) - return - html_part = html.split('

    ', '

    ')) + lyrics = '\n'.join(lyrics_parts) lyrics = lyrics.strip(',"').replace('\\n', '\n') # another odd case: sometimes only that string remains, for # missing songs. this seems to happen after being blocked # above, when filling in the CAPTCHA. if "Instant lyrics for all your music." in lyrics: - return + return None + # sometimes there are non-existent lyrics with some content + if 'Lyrics | Musixmatch' in lyrics: + return None return lyrics @@ -341,87 +328,171 @@ class Genius(Backend): bigishdata.com/2016/09/27/getting-song-lyrics-from-geniuss-api-scraping/ """ + REQUIRES_BS = True + base_url = "https://api.genius.com" def __init__(self, config, log): - super(Genius, self).__init__(config, log) + super().__init__(config, log) self.api_key = config['genius_api_key'].as_str() self.headers = { 'Authorization': "Bearer %s" % self.api_key, 'User-Agent': USER_AGENT, } - def lyrics_from_song_api_path(self, song_api_path): - song_url = self.base_url + song_api_path - response = requests.get(song_url, headers=self.headers) - json = response.json() - path = json["response"]["song"]["path"] - - # Gotta go regular html scraping... come on Genius. - page_url = "https://genius.com" + path - try: - page = requests.get(page_url) - except requests.RequestException as exc: - self._log.debug(u'Genius page request for {0} failed: {1}', - page_url, exc) - return None - html = BeautifulSoup(page.text, "html.parser") - - # Remove script tags that they put in the middle of the lyrics. - [h.extract() for h in html('script')] - - # At least Genius is nice and has a tag called 'lyrics'! - # Updated css where the lyrics are based in HTML. - lyrics = html.find("div", class_="lyrics").get_text() - - return lyrics - def fetch(self, artist, title): - search_url = self.base_url + "/search" - data = {'q': title} - try: - response = requests.get(search_url, data=data, - headers=self.headers) - except requests.RequestException as exc: - self._log.debug(u'Genius API request failed: {0}', exc) + """Fetch lyrics from genius.com + + Because genius doesn't allow accesssing lyrics via the api, + we first query the api for a url matching our artist & title, + then attempt to scrape that url for the lyrics. + """ + json = self._search(artist, title) + if not json: + self._log.debug('Genius API request returned invalid JSON') return None - try: - json = response.json() - except ValueError: - self._log.debug(u'Genius API request returned invalid JSON') - return None - - song_info = None + # find a matching artist in the json for hit in json["response"]["hits"]: - if hit["result"]["primary_artist"]["name"] == artist: - song_info = hit - break + hit_artist = hit["result"]["primary_artist"]["name"] - if song_info: - song_api_path = song_info["result"]["api_path"] - return self.lyrics_from_song_api_path(song_api_path) + if slug(hit_artist) == slug(artist): + html = self.fetch_url(hit["result"]["url"]) + if not html: + return None + return self._scrape_lyrics_from_html(html) + self._log.debug('Genius failed to find a matching artist for \'{0}\'', + artist) + return None -class LyricsWiki(SymbolsReplaced): - """Fetch lyrics from LyricsWiki.""" + def _search(self, artist, title): + """Searches the genius api for a given artist and title - URL_PATTERN = 'http://lyrics.wikia.com/%s:%s' + https://docs.genius.com/#search-h2 - def fetch(self, artist, title): - url = self.build_url(artist, title) - html = self.fetch_url(url) - if not html: + :returns: json response + """ + search_url = self.base_url + "/search" + data = {'q': title + " " + artist.lower()} + try: + response = requests.get( + search_url, data=data, headers=self.headers) + except requests.RequestException as exc: + self._log.debug('Genius API request failed: {0}', exc) + return None + + try: + return response.json() + except ValueError: + return None + + def _scrape_lyrics_from_html(self, html): + """Scrape lyrics from a given genius.com html""" + + soup = try_parse_html(html) + if not soup: return - # Get the HTML fragment inside the appropriate HTML element and then - # extract the text from it. - html_frag = extract_text_in(html, u"
    ") - if html_frag: - lyrics = _scrape_strip_cruft(html_frag, True) + # Remove script tags that they put in the middle of the lyrics. + [h.extract() for h in soup('script')] - if lyrics and 'Unfortunately, we are not licensed' not in lyrics: - return lyrics + # Most of the time, the page contains a div with class="lyrics" where + # all of the lyrics can be found already correctly formatted + # Sometimes, though, it packages the lyrics into separate divs, most + # likely for easier ad placement + lyrics_div = soup.find("div", class_="lyrics") + if not lyrics_div: + self._log.debug('Received unusual song page html') + verse_div = soup.find("div", + class_=re.compile("Lyrics__Container")) + if not verse_div: + if soup.find("div", + class_=re.compile("LyricsPlaceholder__Message"), + string="This song is an instrumental"): + self._log.debug('Detected instrumental') + return "[Instrumental]" + else: + self._log.debug("Couldn't scrape page using known layouts") + return None + + lyrics_div = verse_div.parent + for br in lyrics_div.find_all("br"): + br.replace_with("\n") + ads = lyrics_div.find_all("div", + class_=re.compile("InreadAd__Container")) + for ad in ads: + ad.replace_with("\n") + + return lyrics_div.get_text() + + +class Tekstowo(Backend): + # Fetch lyrics from Tekstowo.pl. + REQUIRES_BS = True + + BASE_URL = 'http://www.tekstowo.pl' + URL_PATTERN = BASE_URL + '/wyszukaj.html?search-title=%s&search-artist=%s' + + def fetch(self, artist, title): + url = self.build_url(title, artist) + search_results = self.fetch_url(url) + if not search_results: + return None + + song_page_url = self.parse_search_results(search_results) + if not song_page_url: + return None + + song_page_html = self.fetch_url(song_page_url) + if not song_page_html: + return None + + return self.extract_lyrics(song_page_html) + + def parse_search_results(self, html): + html = _scrape_strip_cruft(html) + html = _scrape_merge_paragraphs(html) + + soup = try_parse_html(html) + if not soup: + return None + + content_div = soup.find("div", class_="content") + if not content_div: + return None + + card_div = content_div.find("div", class_="card") + if not card_div: + return None + + song_rows = card_div.find_all("div", class_="box-przeboje") + if not song_rows: + return None + + song_row = song_rows[0] + if not song_row: + return None + + link = song_row.find('a') + if not link: + return None + + return self.BASE_URL + link.get('href') + + def extract_lyrics(self, html): + html = _scrape_strip_cruft(html) + html = _scrape_merge_paragraphs(html) + + soup = try_parse_html(html) + if not soup: + return None + + lyrics_div = soup.find("div", class_="song-text") + if not lyrics_div: + return None + + return lyrics_div.get_text() def remove_credits(text): @@ -446,7 +517,8 @@ def _scrape_strip_cruft(html, plain_text_out=False): html = html.replace('\r', '\n') # Normalize EOL. html = re.sub(r' +', ' ', html) # Whitespaces collapse. html = BREAK_RE.sub('\n', html) #
    eats up surrounding '\n'. - html = re.sub(r'<(script).*?(?s)', '', html) # Strip script tags. + html = re.sub(r'(?s)<(script).*?', '', html) # Strip script tags. + html = re.sub('\u2005', " ", html) # replace unicode with regular space if plain_text_out: # Strip remaining HTML tags html = COMMENT_RE.sub('', html) @@ -466,12 +538,6 @@ def scrape_lyrics_from_html(html): """Scrape lyrics from a URL. If no lyrics can be found, return None instead. """ - if not HAS_BEAUTIFUL_SOUP: - return None - - if not html: - return None - def is_text_notcode(text): length = len(text) return (length > 20 and @@ -481,10 +547,8 @@ def scrape_lyrics_from_html(html): html = _scrape_merge_paragraphs(html) # extract all long text blocks that are not code - try: - soup = BeautifulSoup(html, "html.parser", - parse_only=SoupStrainer(text=is_text_notcode)) - except HTMLParseError: + soup = try_parse_html(html, parse_only=SoupStrainer(text=is_text_notcode)) + if not soup: return None # Get the longest text element (if any). @@ -498,8 +562,10 @@ def scrape_lyrics_from_html(html): class Google(Backend): """Fetch lyrics from Google search results.""" + REQUIRES_BS = True + def __init__(self, config, log): - super(Google, self).__init__(config, log) + super().__init__(config, log) self.api_key = config['google_API_key'].as_str() self.engine_id = config['google_engine_ID'].as_str() @@ -511,7 +577,7 @@ class Google(Backend): bad_triggers_occ = [] nb_lines = text.count('\n') if nb_lines <= 1: - self._log.debug(u"Ignoring too short lyrics '{0}'", text) + self._log.debug("Ignoring too short lyrics '{0}'", text) return False elif nb_lines < 5: bad_triggers_occ.append('too_short') @@ -522,14 +588,14 @@ class Google(Backend): bad_triggers = ['lyrics', 'copyright', 'property', 'links'] if artist: - bad_triggers_occ += [artist] + bad_triggers += [artist] for item in bad_triggers: bad_triggers_occ += [item] * len(re.findall(r'\W%s\W' % item, text, re.I)) if bad_triggers_occ: - self._log.debug(u'Bad triggers detected: {0}', bad_triggers_occ) + self._log.debug('Bad triggers detected: {0}', bad_triggers_occ) return len(bad_triggers_occ) < 2 def slugify(self, text): @@ -537,14 +603,14 @@ class Google(Backend): """ text = re.sub(r"[-'_\s]", '_', text) text = re.sub(r"_+", '_', text).strip('_') - pat = "([^,\(]*)\((.*?)\)" # Remove content within parentheses - text = re.sub(pat, '\g<1>', text).strip() + pat = r"([^,\(]*)\((.*?)\)" # Remove content within parentheses + text = re.sub(pat, r'\g<1>', text).strip() try: text = unicodedata.normalize('NFKD', text).encode('ascii', 'ignore') - text = six.text_type(re.sub('[-\s]+', ' ', text.decode('utf-8'))) + text = str(re.sub(r'[-\s]+', ' ', text.decode('utf-8'))) except UnicodeDecodeError: - self._log.exception(u"Failing to normalize '{0}'", text) + self._log.exception("Failing to normalize '{0}'", text) return text BY_TRANS = ['by', 'par', 'de', 'von'] @@ -556,7 +622,7 @@ class Google(Backend): """ title = self.slugify(title.lower()) artist = self.slugify(artist.lower()) - sitename = re.search(u"//([^/]+)/.*", + sitename = re.search("//([^/]+)/.*", self.slugify(url_link.lower())).group(1) url_title = self.slugify(url_title.lower()) @@ -570,7 +636,7 @@ class Google(Backend): [artist, sitename, sitename.replace('www.', '')] + \ self.LYRICS_TRANS tokens = [re.escape(t) for t in tokens] - song_title = re.sub(u'(%s)' % u'|'.join(tokens), u'', url_title) + song_title = re.sub('(%s)' % '|'.join(tokens), '', url_title) song_title = song_title.strip('_|') typo_ratio = .9 @@ -578,53 +644,57 @@ class Google(Backend): return ratio >= typo_ratio def fetch(self, artist, title): - query = u"%s %s" % (artist, title) - url = u'https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s' \ + query = f"{artist} {title}" + url = 'https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s' \ % (self.api_key, self.engine_id, urllib.parse.quote(query.encode('utf-8'))) data = self.fetch_url(url) if not data: - self._log.debug(u'google backend returned no data') + self._log.debug('google backend returned no data') return None try: data = json.loads(data) except ValueError as exc: - self._log.debug(u'google backend returned malformed JSON: {}', exc) + self._log.debug('google backend returned malformed JSON: {}', exc) if 'error' in data: reason = data['error']['errors'][0]['reason'] - self._log.debug(u'google backend error: {0}', reason) + self._log.debug('google backend error: {0}', reason) return None if 'items' in data.keys(): for item in data['items']: url_link = item['link'] - url_title = item.get('title', u'') + url_title = item.get('title', '') if not self.is_page_candidate(url_link, url_title, title, artist): continue html = self.fetch_url(url_link) + if not html: + continue lyrics = scrape_lyrics_from_html(html) if not lyrics: continue if self.is_lyrics(lyrics, artist): - self._log.debug(u'got lyrics from {0}', + self._log.debug('got lyrics from {0}', item['displayLink']) return lyrics + return None + class LyricsPlugin(plugins.BeetsPlugin): - SOURCES = ['google', 'lyricwiki', 'musixmatch', 'genius'] + SOURCES = ['google', 'musixmatch', 'genius', 'tekstowo'] SOURCE_BACKENDS = { 'google': Google, - 'lyricwiki': LyricsWiki, 'musixmatch': MusiXmatch, 'genius': Genius, + 'tekstowo': Tekstowo, } def __init__(self): - super(LyricsPlugin, self).__init__() + super().__init__() self.import_stages = [self.imported] self.config.add({ 'auto': True, @@ -632,7 +702,7 @@ class LyricsPlugin(plugins.BeetsPlugin): 'bing_lang_from': [], 'bing_lang_to': None, 'google_API_key': None, - 'google_engine_ID': u'009217259823014548361:lndtuqkycfu', + 'google_engine_ID': '009217259823014548361:lndtuqkycfu', 'genius_api_key': "Ryq93pUGm8bM6eUWwD_M3NOFFDAtp2yEE7W" "76V-uFL5jks5dNvcGCdarqFjDhP9c", @@ -648,7 +718,7 @@ class LyricsPlugin(plugins.BeetsPlugin): # State information for the ReST writer. # First, the current artist we're writing. - self.artist = u'Unknown artist' + self.artist = 'Unknown artist' # The current album: False means no album yet. self.album = False # The current rest file content. None means the file is not @@ -659,40 +729,44 @@ class LyricsPlugin(plugins.BeetsPlugin): sources = plugins.sanitize_choices( self.config['sources'].as_str_seq(), available_sources) + if not HAS_BEAUTIFUL_SOUP: + sources = self.sanitize_bs_sources(sources) + if 'google' in sources: if not self.config['google_API_key'].get(): # We log a *debug* message here because the default # configuration includes `google`. This way, the source # is silent by default but can be enabled just by # setting an API key. - self._log.debug(u'Disabling google source: ' - u'no API key configured.') + self._log.debug('Disabling google source: ' + 'no API key configured.') sources.remove('google') - elif not HAS_BEAUTIFUL_SOUP: - self._log.warning(u'To use the google lyrics source, you must ' - u'install the beautifulsoup4 module. See ' - u'the documentation for further details.') - sources.remove('google') - - if 'genius' in sources and not HAS_BEAUTIFUL_SOUP: - self._log.debug( - u'The Genius backend requires BeautifulSoup, which is not ' - u'installed, so the source is disabled.' - ) - sources.remove('genius') self.config['bing_lang_from'] = [ x.lower() for x in self.config['bing_lang_from'].as_str_seq()] self.bing_auth_token = None if not HAS_LANGDETECT and self.config['bing_client_secret'].get(): - self._log.warning(u'To use bing translations, you need to ' - u'install the langdetect module. See the ' - u'documentation for further details.') + self._log.warning('To use bing translations, you need to ' + 'install the langdetect module. See the ' + 'documentation for further details.') self.backends = [self.SOURCE_BACKENDS[source](self.config, self._log) for source in sources] + def sanitize_bs_sources(self, sources): + enabled_sources = [] + for source in sources: + if self.SOURCE_BACKENDS[source].REQUIRES_BS: + self._log.debug('To use the %s lyrics source, you must ' + 'install the beautifulsoup4 module. See ' + 'the documentation for further details.' + % source) + else: + enabled_sources.append(source) + + return enabled_sources + def get_bing_access_token(self): params = { 'client_id': 'beets', @@ -708,30 +782,30 @@ class LyricsPlugin(plugins.BeetsPlugin): if 'access_token' in oauth_token: return "Bearer " + oauth_token['access_token'] else: - self._log.warning(u'Could not get Bing Translate API access token.' - u' Check your "bing_client_secret" password') + self._log.warning('Could not get Bing Translate API access token.' + ' Check your "bing_client_secret" password') def commands(self): cmd = ui.Subcommand('lyrics', help='fetch song lyrics') cmd.parser.add_option( - u'-p', u'--print', dest='printlyr', + '-p', '--print', dest='printlyr', action='store_true', default=False, - help=u'print lyrics to console', + help='print lyrics to console', ) cmd.parser.add_option( - u'-r', u'--write-rest', dest='writerest', + '-r', '--write-rest', dest='writerest', action='store', default=None, metavar='dir', - help=u'write lyrics to given directory as ReST files', + help='write lyrics to given directory as ReST files', ) cmd.parser.add_option( - u'-f', u'--force', dest='force_refetch', + '-f', '--force', dest='force_refetch', action='store_true', default=False, - help=u'always re-download lyrics', + help='always re-download lyrics', ) cmd.parser.add_option( - u'-l', u'--local', dest='local_only', + '-l', '--local', dest='local_only', action='store_true', default=False, - help=u'do not fetch missing lyrics', + help='do not fetch missing lyrics', ) def func(lib, opts, args): @@ -740,7 +814,8 @@ class LyricsPlugin(plugins.BeetsPlugin): write = ui.should_write() if opts.writerest: self.writerest_indexes(opts.writerest) - for item in lib.items(ui.decargs(args)): + items = lib.items(ui.decargs(args)) + for item in items: if not opts.local_only and not self.config['local']: self.fetch_item_lyrics( lib, item, write, @@ -750,51 +825,55 @@ class LyricsPlugin(plugins.BeetsPlugin): if opts.printlyr: ui.print_(item.lyrics) if opts.writerest: - self.writerest(opts.writerest, item) - if opts.writerest: - # flush last artist - self.writerest(opts.writerest, None) - ui.print_(u'ReST files generated. to build, use one of:') - ui.print_(u' sphinx-build -b html %s _build/html' + self.appendrest(opts.writerest, item) + if opts.writerest and items: + # flush last artist & write to ReST + self.writerest(opts.writerest) + ui.print_('ReST files generated. to build, use one of:') + ui.print_(' sphinx-build -b html %s _build/html' % opts.writerest) - ui.print_(u' sphinx-build -b epub %s _build/epub' + ui.print_(' sphinx-build -b epub %s _build/epub' % opts.writerest) - ui.print_((u' sphinx-build -b latex %s _build/latex ' - u'&& make -C _build/latex all-pdf') + ui.print_((' sphinx-build -b latex %s _build/latex ' + '&& make -C _build/latex all-pdf') % opts.writerest) cmd.func = func return [cmd] - def writerest(self, directory, item): - """Write the item to an ReST file + def appendrest(self, directory, item): + """Append the item to an ReST file This will keep state (in the `rest` variable) in order to avoid writing continuously to the same files. """ - if item is None or slug(self.artist) != slug(item.albumartist): - if self.rest is not None: - path = os.path.join(directory, 'artists', - slug(self.artist) + u'.rst') - with open(path, 'wb') as output: - output.write(self.rest.encode('utf-8')) - self.rest = None - if item is None: - return + if slug(self.artist) != slug(item.albumartist): + # Write current file and start a new one ~ item.albumartist + self.writerest(directory) self.artist = item.albumartist.strip() - self.rest = u"%s\n%s\n\n.. contents::\n :local:\n\n" \ + self.rest = "%s\n%s\n\n.. contents::\n :local:\n\n" \ % (self.artist, - u'=' * len(self.artist)) + '=' * len(self.artist)) + if self.album != item.album: tmpalbum = self.album = item.album.strip() if self.album == '': - tmpalbum = u'Unknown album' - self.rest += u"%s\n%s\n\n" % (tmpalbum, u'-' * len(tmpalbum)) - title_str = u":index:`%s`" % item.title.strip() - block = u'| ' + item.lyrics.replace(u'\n', u'\n| ') - self.rest += u"%s\n%s\n\n%s\n\n" % (title_str, - u'~' * len(title_str), - block) + tmpalbum = 'Unknown album' + self.rest += "{}\n{}\n\n".format(tmpalbum, '-' * len(tmpalbum)) + title_str = ":index:`%s`" % item.title.strip() + block = '| ' + item.lyrics.replace('\n', '\n| ') + self.rest += "{}\n{}\n\n{}\n\n".format(title_str, + '~' * len(title_str), + block) + + def writerest(self, directory): + """Write self.rest to a ReST file + """ + if self.rest is not None and self.artist is not None: + path = os.path.join(directory, 'artists', + slug(self.artist) + '.rst') + with open(path, 'wb') as output: + output.write(self.rest.encode('utf-8')) def writerest_indexes(self, directory): """Write conf.py and index.rst files necessary for Sphinx @@ -832,7 +911,7 @@ class LyricsPlugin(plugins.BeetsPlugin): """ # Skip if the item already has lyrics. if not force and item.lyrics: - self._log.info(u'lyrics already present: {0}', item) + self._log.info('lyrics already present: {0}', item) return lyrics = None @@ -841,10 +920,10 @@ class LyricsPlugin(plugins.BeetsPlugin): if any(lyrics): break - lyrics = u"\n\n---\n\n".join([l for l in lyrics if l]) + lyrics = "\n\n---\n\n".join([l for l in lyrics if l]) if lyrics: - self._log.info(u'fetched lyrics: {0}', item) + self._log.info('fetched lyrics: {0}', item) if HAS_LANGDETECT and self.config['bing_client_secret'].get(): lang_from = langdetect.detect(lyrics) if self.config['bing_lang_to'].get() != lang_from and ( @@ -854,7 +933,7 @@ class LyricsPlugin(plugins.BeetsPlugin): lyrics = self.append_translation( lyrics, self.config['bing_lang_to']) else: - self._log.info(u'lyrics not found: {0}', item) + self._log.info('lyrics not found: {0}', item) fallback = self.config['fallback'].get() if fallback: lyrics = fallback @@ -872,12 +951,12 @@ class LyricsPlugin(plugins.BeetsPlugin): for backend in self.backends: lyrics = backend.fetch(artist, title) if lyrics: - self._log.debug(u'got lyrics from backend: {0}', + self._log.debug('got lyrics from backend: {0}', backend.__class__.__name__) return _scrape_strip_cruft(lyrics, True) def append_translation(self, text, to_lang): - import xml.etree.ElementTree as ET + from xml.etree import ElementTree if not self.bing_auth_token: self.bing_auth_token = self.get_bing_access_token() @@ -895,10 +974,11 @@ class LyricsPlugin(plugins.BeetsPlugin): self.bing_auth_token = None return self.append_translation(text, to_lang) return text - lines_translated = ET.fromstring(r.text.encode('utf-8')).text + lines_translated = ElementTree.fromstring( + r.text.encode('utf-8')).text # Use a translation mapping dict to build resulting lyrics translations = dict(zip(text_lines, lines_translated.split('|'))) result = '' for line in text.split('\n'): - result += '%s / %s\n' % (line, translations[line]) + result += '{} / {}\n'.format(line, translations[line]) return result diff --git a/libs/common/beetsplug/mbcollection.py b/libs/common/beetsplug/mbcollection.py index d99c386c..f4a0d161 100644 --- a/libs/common/beetsplug/mbcollection.py +++ b/libs/common/beetsplug/mbcollection.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright (c) 2011, Jeffrey Aylesworth # @@ -13,7 +12,6 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets.ui import Subcommand @@ -34,11 +32,11 @@ def mb_call(func, *args, **kwargs): try: return func(*args, **kwargs) except musicbrainzngs.AuthenticationError: - raise ui.UserError(u'authentication with MusicBrainz failed') + raise ui.UserError('authentication with MusicBrainz failed') except (musicbrainzngs.ResponseError, musicbrainzngs.NetworkError) as exc: - raise ui.UserError(u'MusicBrainz API error: {0}'.format(exc)) + raise ui.UserError(f'MusicBrainz API error: {exc}') except musicbrainzngs.UsageError: - raise ui.UserError(u'MusicBrainz credentials missing') + raise ui.UserError('MusicBrainz credentials missing') def submit_albums(collection_id, release_ids): @@ -55,7 +53,7 @@ def submit_albums(collection_id, release_ids): class MusicBrainzCollectionPlugin(BeetsPlugin): def __init__(self): - super(MusicBrainzCollectionPlugin, self).__init__() + super().__init__() config['musicbrainz']['pass'].redact = True musicbrainzngs.auth( config['musicbrainz']['user'].as_str(), @@ -63,7 +61,7 @@ class MusicBrainzCollectionPlugin(BeetsPlugin): ) self.config.add({ 'auto': False, - 'collection': u'', + 'collection': '', 'remove': False, }) if self.config['auto']: @@ -72,18 +70,18 @@ class MusicBrainzCollectionPlugin(BeetsPlugin): def _get_collection(self): collections = mb_call(musicbrainzngs.get_collections) if not collections['collection-list']: - raise ui.UserError(u'no collections exist for user') + raise ui.UserError('no collections exist for user') # Get all collection IDs, avoiding event collections collection_ids = [x['id'] for x in collections['collection-list']] if not collection_ids: - raise ui.UserError(u'No collection found.') + raise ui.UserError('No collection found.') # Check that the collection exists so we can present a nice error collection = self.config['collection'].as_str() if collection: if collection not in collection_ids: - raise ui.UserError(u'invalid collection ID: {}' + raise ui.UserError('invalid collection ID: {}' .format(collection)) return collection @@ -110,7 +108,7 @@ class MusicBrainzCollectionPlugin(BeetsPlugin): def commands(self): mbupdate = Subcommand('mbupdate', - help=u'Update MusicBrainz collection') + help='Update MusicBrainz collection') mbupdate.parser.add_option('-r', '--remove', action='store_true', default=None, @@ -120,7 +118,7 @@ class MusicBrainzCollectionPlugin(BeetsPlugin): return [mbupdate] def remove_missing(self, collection_id, lib_albums): - lib_ids = set([x.mb_albumid for x in lib_albums]) + lib_ids = {x.mb_albumid for x in lib_albums} albums_in_collection = self._get_albums_in_collection(collection_id) remove_me = list(set(albums_in_collection) - lib_ids) for i in range(0, len(remove_me), FETCH_CHUNK_SIZE): @@ -154,13 +152,13 @@ class MusicBrainzCollectionPlugin(BeetsPlugin): if re.match(UUID_REGEX, aid): album_ids.append(aid) else: - self._log.info(u'skipping invalid MBID: {0}', aid) + self._log.info('skipping invalid MBID: {0}', aid) # Submit to MusicBrainz. self._log.info( - u'Updating MusicBrainz collection {0}...', collection_id + 'Updating MusicBrainz collection {0}...', collection_id ) submit_albums(collection_id, album_ids) if remove_missing: self.remove_missing(collection_id, lib.albums()) - self._log.info(u'...MusicBrainz collection updated.') + self._log.info('...MusicBrainz collection updated.') diff --git a/libs/common/beetsplug/mbsubmit.py b/libs/common/beetsplug/mbsubmit.py index 02bd5f69..3ede0125 100644 --- a/libs/common/beetsplug/mbsubmit.py +++ b/libs/common/beetsplug/mbsubmit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson and Diego Moreda. # @@ -19,11 +18,9 @@ This plugin allows the user to print track information in a format that is parseable by the MusicBrainz track parser [1]. Programmatic submitting is not implemented by MusicBrainz yet. -[1] http://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings +[1] https://wiki.musicbrainz.org/History:How_To_Parse_Track_Listings """ -from __future__ import division, absolute_import, print_function - from beets.autotag import Recommendation from beets.plugins import BeetsPlugin @@ -33,10 +30,10 @@ from beetsplug.info import print_data class MBSubmitPlugin(BeetsPlugin): def __init__(self): - super(MBSubmitPlugin, self).__init__() + super().__init__() self.config.add({ - 'format': u'$track. $title - $artist ($length)', + 'format': '$track. $title - $artist ($length)', 'threshold': 'medium', }) @@ -53,7 +50,7 @@ class MBSubmitPlugin(BeetsPlugin): def before_choose_candidate_event(self, session, task): if task.rec <= self.threshold: - return [PromptChoice(u'p', u'Print tracks', self.print_tracks)] + return [PromptChoice('p', 'Print tracks', self.print_tracks)] def print_tracks(self, session, task): for i in sorted(task.items, key=lambda i: i.track): diff --git a/libs/common/beetsplug/mbsync.py b/libs/common/beetsplug/mbsync.py index 1764a177..26778830 100644 --- a/libs/common/beetsplug/mbsync.py +++ b/libs/common/beetsplug/mbsync.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Jakob Schnitzer. # @@ -15,47 +14,37 @@ """Update library's tags using MusicBrainz. """ -from __future__ import division, absolute_import, print_function -from beets.plugins import BeetsPlugin +from beets.plugins import BeetsPlugin, apply_item_changes from beets import autotag, library, ui, util from beets.autotag import hooks from collections import defaultdict +import re -def apply_item_changes(lib, item, move, pretend, write): - """Store, move and write the item according to the arguments. - """ - if not pretend: - # Move the item if it's in the library. - if move and lib.directory in util.ancestry(item.path): - item.move(with_album=False) - - if write: - item.try_write() - item.store() +MBID_REGEX = r"(\d|\w){8}-(\d|\w){4}-(\d|\w){4}-(\d|\w){4}-(\d|\w){12}" class MBSyncPlugin(BeetsPlugin): def __init__(self): - super(MBSyncPlugin, self).__init__() + super().__init__() def commands(self): cmd = ui.Subcommand('mbsync', - help=u'update metadata from musicbrainz') + help='update metadata from musicbrainz') cmd.parser.add_option( - u'-p', u'--pretend', action='store_true', - help=u'show all changes but do nothing') + '-p', '--pretend', action='store_true', + help='show all changes but do nothing') cmd.parser.add_option( - u'-m', u'--move', action='store_true', dest='move', - help=u"move files in the library directory") + '-m', '--move', action='store_true', dest='move', + help="move files in the library directory") cmd.parser.add_option( - u'-M', u'--nomove', action='store_false', dest='move', - help=u"don't move files in library") + '-M', '--nomove', action='store_false', dest='move', + help="don't move files in library") cmd.parser.add_option( - u'-W', u'--nowrite', action='store_false', + '-W', '--nowrite', action='store_false', default=None, dest='write', - help=u"don't write updated metadata to files") + help="don't write updated metadata to files") cmd.parser.add_format_option() cmd.func = self.func return [cmd] @@ -75,17 +64,23 @@ class MBSyncPlugin(BeetsPlugin): """Retrieve and apply info from the autotagger for items matched by query. """ - for item in lib.items(query + [u'singleton:true']): + for item in lib.items(query + ['singleton:true']): item_formatted = format(item) if not item.mb_trackid: - self._log.info(u'Skipping singleton with no mb_trackid: {0}', + self._log.info('Skipping singleton with no mb_trackid: {0}', item_formatted) continue + # Do we have a valid MusicBrainz track ID? + if not re.match(MBID_REGEX, item.mb_trackid): + self._log.info('Skipping singleton with invalid mb_trackid:' + + ' {0}', item_formatted) + continue + # Get the MusicBrainz recording info. track_info = hooks.track_for_mbid(item.mb_trackid) if not track_info: - self._log.info(u'Recording ID not found: {0} for track {0}', + self._log.info('Recording ID not found: {0} for track {0}', item.mb_trackid, item_formatted) continue @@ -103,16 +98,22 @@ class MBSyncPlugin(BeetsPlugin): for a in lib.albums(query): album_formatted = format(a) if not a.mb_albumid: - self._log.info(u'Skipping album with no mb_albumid: {0}', + self._log.info('Skipping album with no mb_albumid: {0}', album_formatted) continue items = list(a.items()) + # Do we have a valid MusicBrainz album ID? + if not re.match(MBID_REGEX, a.mb_albumid): + self._log.info('Skipping album with invalid mb_albumid: {0}', + album_formatted) + continue + # Get the MusicBrainz album information. album_info = hooks.album_for_mbid(a.mb_albumid) if not album_info: - self._log.info(u'Release ID {0} not found for album {1}', + self._log.info('Release ID {0} not found for album {1}', a.mb_albumid, album_formatted) continue @@ -120,7 +121,7 @@ class MBSyncPlugin(BeetsPlugin): # Map release track and recording MBIDs to their information. # Recordings can appear multiple times on a release, so each MBID # maps to a list of TrackInfo objects. - releasetrack_index = dict() + releasetrack_index = {} track_index = defaultdict(list) for track_info in album_info.tracks: releasetrack_index[track_info.release_track_id] = track_info @@ -148,7 +149,7 @@ class MBSyncPlugin(BeetsPlugin): break # Apply. - self._log.debug(u'applying changes to {}', album_formatted) + self._log.debug('applying changes to {}', album_formatted) with lib.transaction(): autotag.apply_metadata(album_info, mapping) changed = False @@ -173,5 +174,5 @@ class MBSyncPlugin(BeetsPlugin): # Move album art (and any inconsistent items). if move and lib.directory in util.ancestry(items[0].path): - self._log.debug(u'moving album {0}', album_formatted) + self._log.debug('moving album {0}', album_formatted) a.move() diff --git a/libs/common/beetsplug/metasync/__init__.py b/libs/common/beetsplug/metasync/__init__.py index 02f0b0f9..361071fb 100644 --- a/libs/common/beetsplug/metasync/__init__.py +++ b/libs/common/beetsplug/metasync/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Heinz Wiesinger. # @@ -16,15 +15,13 @@ """Synchronize information from music player libraries """ -from __future__ import division, absolute_import, print_function from abc import abstractmethod, ABCMeta from importlib import import_module -from beets.util.confit import ConfigValueError +from confuse import ConfigValueError from beets import ui from beets.plugins import BeetsPlugin -import six METASYNC_MODULE = 'beetsplug.metasync' @@ -36,7 +33,7 @@ SOURCES = { } -class MetaSource(six.with_metaclass(ABCMeta, object)): +class MetaSource(metaclass=ABCMeta): def __init__(self, config, log): self.item_types = {} self.config = config @@ -77,7 +74,7 @@ class MetaSyncPlugin(BeetsPlugin): item_types = load_item_types() def __init__(self): - super(MetaSyncPlugin, self).__init__() + super().__init__() def commands(self): cmd = ui.Subcommand('metasync', @@ -108,7 +105,7 @@ class MetaSyncPlugin(BeetsPlugin): # Avoid needlessly instantiating meta sources (can be expensive) if not items: - self._log.info(u'No items found matching query') + self._log.info('No items found matching query') return # Instantiate the meta sources @@ -116,18 +113,18 @@ class MetaSyncPlugin(BeetsPlugin): try: cls = META_SOURCES[player] except KeyError: - self._log.error(u'Unknown metadata source \'{0}\''.format( + self._log.error('Unknown metadata source \'{}\''.format( player)) try: meta_source_instances[player] = cls(self.config, self._log) except (ImportError, ConfigValueError) as e: - self._log.error(u'Failed to instantiate metadata source ' - u'\'{0}\': {1}'.format(player, e)) + self._log.error('Failed to instantiate metadata source ' + '\'{}\': {}'.format(player, e)) # Avoid needlessly iterating over items if not meta_source_instances: - self._log.error(u'No valid metadata sources found') + self._log.error('No valid metadata sources found') return # Sync the items with all of the meta sources diff --git a/libs/common/beetsplug/metasync/amarok.py b/libs/common/beetsplug/metasync/amarok.py index 0622fc17..a49eecc3 100644 --- a/libs/common/beetsplug/metasync/amarok.py +++ b/libs/common/beetsplug/metasync/amarok.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Heinz Wiesinger. # @@ -16,7 +15,6 @@ """Synchronize information from amarok's library via dbus """ -from __future__ import division, absolute_import, print_function from os.path import basename from datetime import datetime @@ -49,14 +47,14 @@ class Amarok(MetaSource): 'amarok_lastplayed': DateType(), } - queryXML = u' \ + query_xml = ' \ \ \ \ ' def __init__(self, config, log): - super(Amarok, self).__init__(config, log) + super().__init__(config, log) if not dbus: raise ImportError('failed to import dbus') @@ -72,7 +70,7 @@ class Amarok(MetaSource): # of the result set. So query for the filename and then try to match # the correct item from the results we get back results = self.collection.Query( - self.queryXML % quoteattr(basename(path)) + self.query_xml % quoteattr(basename(path)) ) for result in results: if result['xesam:url'] != path: diff --git a/libs/common/beetsplug/metasync/itunes.py b/libs/common/beetsplug/metasync/itunes.py index 17ab1637..e50a5713 100644 --- a/libs/common/beetsplug/metasync/itunes.py +++ b/libs/common/beetsplug/metasync/itunes.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Tom Jaspers. # @@ -16,7 +15,6 @@ """Synchronize information from iTunes's library """ -from __future__ import division, absolute_import, print_function from contextlib import contextmanager import os @@ -24,13 +22,13 @@ import shutil import tempfile import plistlib -from six.moves.urllib.parse import urlparse, unquote +from urllib.parse import urlparse, unquote from time import mktime from beets import util from beets.dbcore import types from beets.library import DateType -from beets.util.confit import ConfigValueError +from confuse import ConfigValueError from beetsplug.metasync import MetaSource @@ -63,15 +61,16 @@ def _norm_itunes_path(path): class Itunes(MetaSource): item_types = { - 'itunes_rating': types.INTEGER, # 0..100 scale - 'itunes_playcount': types.INTEGER, - 'itunes_skipcount': types.INTEGER, - 'itunes_lastplayed': DateType(), + 'itunes_rating': types.INTEGER, # 0..100 scale + 'itunes_playcount': types.INTEGER, + 'itunes_skipcount': types.INTEGER, + 'itunes_lastplayed': DateType(), 'itunes_lastskipped': DateType(), + 'itunes_dateadded': DateType(), } def __init__(self, config, log): - super(Itunes, self).__init__(config, log) + super().__init__(config, log) config.add({'itunes': { 'library': '~/Music/iTunes/iTunes Library.xml' @@ -82,19 +81,20 @@ class Itunes(MetaSource): try: self._log.debug( - u'loading iTunes library from {0}'.format(library_path)) + f'loading iTunes library from {library_path}') with create_temporary_copy(library_path) as library_copy: - raw_library = plistlib.readPlist(library_copy) - except IOError as e: - raise ConfigValueError(u'invalid iTunes library: ' + e.strerror) + with open(library_copy, 'rb') as library_copy_f: + raw_library = plistlib.load(library_copy_f) + except OSError as e: + raise ConfigValueError('invalid iTunes library: ' + e.strerror) except Exception: # It's likely the user configured their '.itl' library (<> xml) if os.path.splitext(library_path)[1].lower() != '.xml': - hint = u': please ensure that the configured path' \ - u' points to the .XML library' + hint = ': please ensure that the configured path' \ + ' points to the .XML library' else: hint = '' - raise ConfigValueError(u'invalid iTunes library' + hint) + raise ConfigValueError('invalid iTunes library' + hint) # Make the iTunes library queryable using the path self.collection = {_norm_itunes_path(track['Location']): track @@ -105,7 +105,7 @@ class Itunes(MetaSource): result = self.collection.get(util.bytestring_path(item.path).lower()) if not result: - self._log.warning(u'no iTunes match found for {0}'.format(item)) + self._log.warning(f'no iTunes match found for {item}') return item.itunes_rating = result.get('Rating') @@ -119,3 +119,7 @@ class Itunes(MetaSource): if result.get('Skip Date'): item.itunes_lastskipped = mktime( result.get('Skip Date').timetuple()) + + if result.get('Date Added'): + item.itunes_dateadded = mktime( + result.get('Date Added').timetuple()) diff --git a/libs/common/beetsplug/missing.py b/libs/common/beetsplug/missing.py index 8f0790f2..771978c1 100644 --- a/libs/common/beetsplug/missing.py +++ b/libs/common/beetsplug/missing.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Pedro Silva. # Copyright 2017, Quentin Young. @@ -16,7 +15,6 @@ """List missing tracks. """ -from __future__ import division, absolute_import, print_function import musicbrainzngs @@ -93,7 +91,7 @@ class MissingPlugin(BeetsPlugin): } def __init__(self): - super(MissingPlugin, self).__init__() + super().__init__() self.config.add({ 'count': False, @@ -107,14 +105,14 @@ class MissingPlugin(BeetsPlugin): help=__doc__, aliases=['miss']) self._command.parser.add_option( - u'-c', u'--count', dest='count', action='store_true', - help=u'count missing tracks per album') + '-c', '--count', dest='count', action='store_true', + help='count missing tracks per album') self._command.parser.add_option( - u'-t', u'--total', dest='total', action='store_true', - help=u'count total of missing tracks') + '-t', '--total', dest='total', action='store_true', + help='count total of missing tracks') self._command.parser.add_option( - u'-a', u'--album', dest='album', action='store_true', - help=u'show missing albums for artist instead of tracks') + '-a', '--album', dest='album', action='store_true', + help='show missing albums for artist instead of tracks') self._command.parser.add_format_option() def commands(self): @@ -173,10 +171,10 @@ class MissingPlugin(BeetsPlugin): # build dict mapping artist to list of all albums for artist, albums in albums_by_artist.items(): if artist[1] is None or artist[1] == "": - albs_no_mbid = [u"'" + a['album'] + u"'" for a in albums] + albs_no_mbid = ["'" + a['album'] + "'" for a in albums] self._log.info( - u"No musicbrainz ID for artist '{}' found in album(s) {}; " - "skipping", artist[0], u", ".join(albs_no_mbid) + "No musicbrainz ID for artist '{}' found in album(s) {}; " + "skipping", artist[0], ", ".join(albs_no_mbid) ) continue @@ -185,7 +183,7 @@ class MissingPlugin(BeetsPlugin): release_groups = resp['release-group-list'] except MusicBrainzError as err: self._log.info( - u"Couldn't fetch info for artist '{}' ({}) - '{}'", + "Couldn't fetch info for artist '{}' ({}) - '{}'", artist[0], artist[1], err ) continue @@ -207,7 +205,7 @@ class MissingPlugin(BeetsPlugin): missing_titles = {rg['title'] for rg in missing} for release_title in missing_titles: - print_(u"{} - {}".format(artist[0], release_title)) + print_("{} - {}".format(artist[0], release_title)) if total: print(total_missing) @@ -216,13 +214,13 @@ class MissingPlugin(BeetsPlugin): """Query MusicBrainz to determine items missing from `album`. """ item_mbids = [x.mb_trackid for x in album.items()] - if len([i for i in album.items()]) < album.albumtotal: + if len(list(album.items())) < album.albumtotal: # fetch missing items # TODO: Implement caching that without breaking other stuff album_info = hooks.album_for_mbid(album.mb_albumid) for track_info in getattr(album_info, 'tracks', []): if track_info.track_id not in item_mbids: item = _item(track_info, album_info, album.id) - self._log.debug(u'track {0} in album {1}', + self._log.debug('track {0} in album {1}', track_info.track_id, album_info.album_id) yield item diff --git a/libs/common/beetsplug/mpdstats.py b/libs/common/beetsplug/mpdstats.py index e5e82d48..96291cf4 100644 --- a/libs/common/beetsplug/mpdstats.py +++ b/libs/common/beetsplug/mpdstats.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Peter Schnebel and Johann Klähn. # @@ -13,11 +12,8 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function import mpd -import socket -import select import time import os @@ -45,14 +41,21 @@ def is_url(path): return path.split('://', 1)[0] in ['http', 'https'] -class MPDClientWrapper(object): +class MPDClientWrapper: def __init__(self, log): self._log = log - self.music_directory = ( - mpd_config['music_directory'].as_str()) + self.music_directory = mpd_config['music_directory'].as_str() + self.strip_path = mpd_config['strip_path'].as_str() - self.client = mpd.MPDClient(use_unicode=True) + # Ensure strip_path end with '/' + if not self.strip_path.endswith('/'): + self.strip_path += '/' + + self._log.debug('music_directory: {0}', self.music_directory) + self._log.debug('strip_path: {0}', self.strip_path) + + self.client = mpd.MPDClient() def connect(self): """Connect to the MPD. @@ -63,11 +66,11 @@ class MPDClientWrapper(object): if host[0] in ['/', '~']: host = os.path.expanduser(host) - self._log.info(u'connecting to {0}:{1}', host, port) + self._log.info('connecting to {0}:{1}', host, port) try: self.client.connect(host, port) - except socket.error as e: - raise ui.UserError(u'could not connect to MPD: {0}'.format(e)) + except OSError as e: + raise ui.UserError(f'could not connect to MPD: {e}') password = mpd_config['password'].as_str() if password: @@ -75,7 +78,7 @@ class MPDClientWrapper(object): self.client.password(password) except mpd.CommandError as e: raise ui.UserError( - u'could not authenticate to MPD: {0}'.format(e) + f'could not authenticate to MPD: {e}' ) def disconnect(self): @@ -90,12 +93,12 @@ class MPDClientWrapper(object): """ try: return getattr(self.client, command)() - except (select.error, mpd.ConnectionError) as err: - self._log.error(u'{0}', err) + except (OSError, mpd.ConnectionError) as err: + self._log.error('{0}', err) if retries <= 0: # if we exited without breaking, we couldn't reconnect in time :( - raise ui.UserError(u'communication with MPD server failed') + raise ui.UserError('communication with MPD server failed') time.sleep(RETRY_INTERVAL) @@ -107,18 +110,26 @@ class MPDClientWrapper(object): self.connect() return self.get(command, retries=retries - 1) - def playlist(self): - """Return the currently active playlist. Prefixes paths with the - music_directory, to get the absolute path. + def currentsong(self): + """Return the path to the currently playing song, along with its + songid. Prefixes paths with the music_directory, to get the absolute + path. + In some cases, we need to remove the local path from MPD server, + we replace 'strip_path' with ''. + `strip_path` defaults to ''. """ - result = {} - for entry in self.get('playlistinfo'): + result = None + entry = self.get('currentsong') + if 'file' in entry: if not is_url(entry['file']): - result[entry['id']] = os.path.join( - self.music_directory, entry['file']) + file = entry['file'] + if file.startswith(self.strip_path): + file = file[len(self.strip_path):] + result = os.path.join(self.music_directory, file) else: - result[entry['id']] = entry['file'] - return result + result = entry['file'] + self._log.debug('returning: {0}', result) + return result, entry.get('id') def status(self): """Return the current status of the MPD. @@ -132,7 +143,7 @@ class MPDClientWrapper(object): return self.get('idle') -class MPDStats(object): +class MPDStats: def __init__(self, lib, log): self.lib = lib self._log = log @@ -164,7 +175,7 @@ class MPDStats(object): if item: return item else: - self._log.info(u'item not found: {0}', displayable_path(path)) + self._log.info('item not found: {0}', displayable_path(path)) def update_item(self, item, attribute, value=None, increment=None): """Update the beets item. Set attribute to value or increment the value @@ -182,7 +193,7 @@ class MPDStats(object): item[attribute] = value item.store() - self._log.debug(u'updated: {0} = {1} [{2}]', + self._log.debug('updated: {0} = {1} [{2}]', attribute, item[attribute], displayable_path(item.path)) @@ -229,29 +240,31 @@ class MPDStats(object): """Updates the play count of a song. """ self.update_item(song['beets_item'], 'play_count', increment=1) - self._log.info(u'played {0}', displayable_path(song['path'])) + self._log.info('played {0}', displayable_path(song['path'])) def handle_skipped(self, song): """Updates the skip count of a song. """ self.update_item(song['beets_item'], 'skip_count', increment=1) - self._log.info(u'skipped {0}', displayable_path(song['path'])) + self._log.info('skipped {0}', displayable_path(song['path'])) def on_stop(self, status): - self._log.info(u'stop') + self._log.info('stop') - if self.now_playing: + # if the current song stays the same it means that we stopped on the + # current track and should not record a skip. + if self.now_playing and self.now_playing['id'] != status.get('songid'): self.handle_song_change(self.now_playing) self.now_playing = None def on_pause(self, status): - self._log.info(u'pause') + self._log.info('pause') self.now_playing = None def on_play(self, status): - playlist = self.mpd.playlist() - path = playlist.get(status['songid']) + + path, songid = self.mpd.currentsong() if not path: return @@ -276,16 +289,17 @@ class MPDStats(object): self.handle_song_change(self.now_playing) if is_url(path): - self._log.info(u'playing stream {0}', displayable_path(path)) + self._log.info('playing stream {0}', displayable_path(path)) self.now_playing = None return - self._log.info(u'playing {0}', displayable_path(path)) + self._log.info('playing {0}', displayable_path(path)) self.now_playing = { - 'started': time.time(), - 'remaining': remaining, - 'path': path, + 'started': time.time(), + 'remaining': remaining, + 'path': path, + 'id': songid, 'beets_item': self.get_item(path), } @@ -305,7 +319,7 @@ class MPDStats(object): if handler: handler(status) else: - self._log.debug(u'unhandled status "{0}"', status) + self._log.debug('unhandled status "{0}"', status) events = self.mpd.events() @@ -313,37 +327,38 @@ class MPDStats(object): class MPDStatsPlugin(plugins.BeetsPlugin): item_types = { - 'play_count': types.INTEGER, - 'skip_count': types.INTEGER, + 'play_count': types.INTEGER, + 'skip_count': types.INTEGER, 'last_played': library.DateType(), - 'rating': types.FLOAT, + 'rating': types.FLOAT, } def __init__(self): - super(MPDStatsPlugin, self).__init__() + super().__init__() mpd_config.add({ 'music_directory': config['directory'].as_filename(), - 'rating': True, - 'rating_mix': 0.75, - 'host': os.environ.get('MPD_HOST', u'localhost'), - 'port': 6600, - 'password': u'', + 'strip_path': '', + 'rating': True, + 'rating_mix': 0.75, + 'host': os.environ.get('MPD_HOST', 'localhost'), + 'port': int(os.environ.get('MPD_PORT', 6600)), + 'password': '', }) mpd_config['password'].redact = True def commands(self): cmd = ui.Subcommand( 'mpdstats', - help=u'run a MPD client to gather play statistics') + help='run a MPD client to gather play statistics') cmd.parser.add_option( - u'--host', dest='host', type='string', - help=u'set the hostname of the server to connect to') + '--host', dest='host', type='string', + help='set the hostname of the server to connect to') cmd.parser.add_option( - u'--port', dest='port', type='int', - help=u'set the port of the MPD server to connect to') + '--port', dest='port', type='int', + help='set the port of the MPD server to connect to') cmd.parser.add_option( - u'--password', dest='password', type='string', - help=u'set the password of the MPD server to connect to') + '--password', dest='password', type='string', + help='set the password of the MPD server to connect to') def func(lib, opts, args): mpd_config.set_args(opts) diff --git a/libs/common/beetsplug/mpdupdate.py b/libs/common/beetsplug/mpdupdate.py index 6ecc9213..e5264e18 100644 --- a/libs/common/beetsplug/mpdupdate.py +++ b/libs/common/beetsplug/mpdupdate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -21,19 +20,17 @@ Put something like the following in your config.yaml to configure: port: 6600 password: seekrit """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin import os import socket from beets import config -import six # No need to introduce a dependency on an MPD library for such a # simple use case. Here's a simple socket abstraction to make things # easier. -class BufferedSocket(object): +class BufferedSocket: """Socket abstraction that allows reading by line.""" def __init__(self, host, port, sep=b'\n'): if host[0] in ['/', '~']: @@ -66,11 +63,11 @@ class BufferedSocket(object): class MPDUpdatePlugin(BeetsPlugin): def __init__(self): - super(MPDUpdatePlugin, self).__init__() + super().__init__() config['mpd'].add({ - 'host': os.environ.get('MPD_HOST', u'localhost'), - 'port': 6600, - 'password': u'', + 'host': os.environ.get('MPD_HOST', 'localhost'), + 'port': int(os.environ.get('MPD_PORT', 6600)), + 'password': '', }) config['mpd']['password'].redact = True @@ -100,21 +97,21 @@ class MPDUpdatePlugin(BeetsPlugin): try: s = BufferedSocket(host, port) - except socket.error as e: - self._log.warning(u'MPD connection failed: {0}', - six.text_type(e.strerror)) + except OSError as e: + self._log.warning('MPD connection failed: {0}', + str(e.strerror)) return resp = s.readline() if b'OK MPD' not in resp: - self._log.warning(u'MPD connection failed: {0!r}', resp) + self._log.warning('MPD connection failed: {0!r}', resp) return if password: s.send(b'password "%s"\n' % password.encode('utf8')) resp = s.readline() if b'OK' not in resp: - self._log.warning(u'Authentication failed: {0!r}', resp) + self._log.warning('Authentication failed: {0!r}', resp) s.send(b'close\n') s.close() return @@ -122,8 +119,8 @@ class MPDUpdatePlugin(BeetsPlugin): s.send(b'update\n') resp = s.readline() if b'updating_db' not in resp: - self._log.warning(u'Update failed: {0!r}', resp) + self._log.warning('Update failed: {0!r}', resp) s.send(b'close\n') s.close() - self._log.info(u'Database updated.') + self._log.info('Database updated.') diff --git a/libs/common/beetsplug/parentwork.py b/libs/common/beetsplug/parentwork.py new file mode 100644 index 00000000..75307b8f --- /dev/null +++ b/libs/common/beetsplug/parentwork.py @@ -0,0 +1,211 @@ +# This file is part of beets. +# Copyright 2017, Dorian Soergel. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Gets parent work, its disambiguation and id, composer, composer sort name +and work composition date +""" + + +from beets import ui +from beets.plugins import BeetsPlugin + +import musicbrainzngs + + +def direct_parent_id(mb_workid, work_date=None): + """Given a Musicbrainz work id, find the id one of the works the work is + part of and the first composition date it encounters. + """ + work_info = musicbrainzngs.get_work_by_id(mb_workid, + includes=["work-rels", + "artist-rels"]) + if 'artist-relation-list' in work_info['work'] and work_date is None: + for artist in work_info['work']['artist-relation-list']: + if artist['type'] == 'composer': + if 'end' in artist.keys(): + work_date = artist['end'] + + if 'work-relation-list' in work_info['work']: + for direct_parent in work_info['work']['work-relation-list']: + if direct_parent['type'] == 'parts' \ + and direct_parent.get('direction') == 'backward': + direct_id = direct_parent['work']['id'] + return direct_id, work_date + return None, work_date + + +def work_parent_id(mb_workid): + """Find the parent work id and composition date of a work given its id. + """ + work_date = None + while True: + new_mb_workid, work_date = direct_parent_id(mb_workid, work_date) + if not new_mb_workid: + return mb_workid, work_date + mb_workid = new_mb_workid + return mb_workid, work_date + + +def find_parentwork_info(mb_workid): + """Get the MusicBrainz information dict about a parent work, including + the artist relations, and the composition date for a work's parent work. + """ + parent_id, work_date = work_parent_id(mb_workid) + work_info = musicbrainzngs.get_work_by_id(parent_id, + includes=["artist-rels"]) + return work_info, work_date + + +class ParentWorkPlugin(BeetsPlugin): + def __init__(self): + super().__init__() + + self.config.add({ + 'auto': False, + 'force': False, + }) + + if self.config['auto']: + self.import_stages = [self.imported] + + def commands(self): + + def func(lib, opts, args): + self.config.set_args(opts) + force_parent = self.config['force'].get(bool) + write = ui.should_write() + + for item in lib.items(ui.decargs(args)): + changed = self.find_work(item, force_parent) + if changed: + item.store() + if write: + item.try_write() + command = ui.Subcommand( + 'parentwork', + help='fetch parent works, composers and dates') + + command.parser.add_option( + '-f', '--force', dest='force', + action='store_true', default=None, + help='re-fetch when parent work is already present') + + command.func = func + return [command] + + def imported(self, session, task): + """Import hook for fetching parent works automatically. + """ + force_parent = self.config['force'].get(bool) + + for item in task.imported_items(): + self.find_work(item, force_parent) + item.store() + + def get_info(self, item, work_info): + """Given the parent work info dict, fetch parent_composer, + parent_composer_sort, parentwork, parentwork_disambig, mb_workid and + composer_ids. + """ + + parent_composer = [] + parent_composer_sort = [] + parentwork_info = {} + + composer_exists = False + if 'artist-relation-list' in work_info['work']: + for artist in work_info['work']['artist-relation-list']: + if artist['type'] == 'composer': + composer_exists = True + parent_composer.append(artist['artist']['name']) + parent_composer_sort.append(artist['artist']['sort-name']) + if 'end' in artist.keys(): + parentwork_info["parentwork_date"] = artist['end'] + + parentwork_info['parent_composer'] = ', '.join(parent_composer) + parentwork_info['parent_composer_sort'] = ', '.join( + parent_composer_sort) + + if not composer_exists: + self._log.debug( + 'no composer for {}; add one at ' + 'https://musicbrainz.org/work/{}', + item, work_info['work']['id'], + ) + + parentwork_info['parentwork'] = work_info['work']['title'] + parentwork_info['mb_parentworkid'] = work_info['work']['id'] + + if 'disambiguation' in work_info['work']: + parentwork_info['parentwork_disambig'] = work_info[ + 'work']['disambiguation'] + + else: + parentwork_info['parentwork_disambig'] = None + + return parentwork_info + + def find_work(self, item, force): + """Finds the parent work of a recording and populates the tags + accordingly. + + The parent work is found recursively, by finding the direct parent + repeatedly until there are no more links in the chain. We return the + final, topmost work in the chain. + + Namely, the tags parentwork, parentwork_disambig, mb_parentworkid, + parent_composer, parent_composer_sort and work_date are populated. + """ + + if not item.mb_workid: + self._log.info('No work for {}, \ +add one at https://musicbrainz.org/recording/{}', item, item.mb_trackid) + return + + hasparent = hasattr(item, 'parentwork') + work_changed = True + if hasattr(item, 'parentwork_workid_current'): + work_changed = item.parentwork_workid_current != item.mb_workid + if force or not hasparent or work_changed: + try: + work_info, work_date = find_parentwork_info(item.mb_workid) + except musicbrainzngs.musicbrainz.WebServiceError as e: + self._log.debug("error fetching work: {}", e) + return + parent_info = self.get_info(item, work_info) + parent_info['parentwork_workid_current'] = item.mb_workid + if 'parent_composer' in parent_info: + self._log.debug("Work fetched: {} - {}", + parent_info['parentwork'], + parent_info['parent_composer']) + else: + self._log.debug("Work fetched: {} - no parent composer", + parent_info['parentwork']) + + elif hasparent: + self._log.debug("{}: Work present, skipping", item) + return + + # apply all non-null values to the item + for key, value in parent_info.items(): + if value: + item[key] = value + + if work_date: + item['work_date'] = work_date + return ui.show_model_changes( + item, fields=['parentwork', 'parentwork_disambig', + 'mb_parentworkid', 'parent_composer', + 'parent_composer_sort', 'work_date', + 'parentwork_workid_current', 'parentwork_date']) diff --git a/libs/common/beetsplug/permissions.py b/libs/common/beetsplug/permissions.py index dd9e0984..f5aab056 100644 --- a/libs/common/beetsplug/permissions.py +++ b/libs/common/beetsplug/permissions.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import division, absolute_import, print_function - """Fixes file permissions after the file gets written on import. Put something like the following in your config.yaml to configure: @@ -13,7 +9,6 @@ import os from beets import config, util from beets.plugins import BeetsPlugin from beets.util import ancestry -import six def convert_perm(perm): @@ -21,8 +16,8 @@ def convert_perm(perm): Or, if `perm` is an integer, reinterpret it as an octal number that has been "misinterpreted" as decimal. """ - if isinstance(perm, six.integer_types): - perm = six.text_type(perm) + if isinstance(perm, int): + perm = str(perm) return int(perm, 8) @@ -40,11 +35,11 @@ def assert_permissions(path, permission, log): """ if not check_permissions(util.syspath(path), permission): log.warning( - u'could not set permissions on {}', + 'could not set permissions on {}', util.displayable_path(path), ) log.debug( - u'set permissions to {}, but permissions are now {}', + 'set permissions to {}, but permissions are now {}', permission, os.stat(util.syspath(path)).st_mode & 0o777, ) @@ -60,20 +55,39 @@ def dirs_in_library(library, item): class Permissions(BeetsPlugin): def __init__(self): - super(Permissions, self).__init__() + super().__init__() # Adding defaults. self.config.add({ - u'file': '644', - u'dir': '755', + 'file': '644', + 'dir': '755', }) self.register_listener('item_imported', self.fix) self.register_listener('album_imported', self.fix) + self.register_listener('art_set', self.fix_art) def fix(self, lib, item=None, album=None): """Fix the permissions for an imported Item or Album. """ + files = [] + dirs = set() + if item: + files.append(item.path) + dirs.update(dirs_in_library(lib.directory, item.path)) + elif album: + for album_item in album.items(): + files.append(album_item.path) + dirs.update(dirs_in_library(lib.directory, album_item.path)) + self.set_permissions(files=files, dirs=dirs) + + def fix_art(self, album): + """Fix the permission for Album art file. + """ + if album.artpath: + self.set_permissions(files=[album.artpath]) + + def set_permissions(self, files=[], dirs=[]): # Get the configured permissions. The user can specify this either a # string (in YAML quotes) or, for convenience, as an integer so the # quotes can be omitted. In the latter case, we need to reinterpret the @@ -83,21 +97,10 @@ class Permissions(BeetsPlugin): file_perm = convert_perm(file_perm) dir_perm = convert_perm(dir_perm) - # Create chmod_queue. - file_chmod_queue = [] - if item: - file_chmod_queue.append(item.path) - elif album: - for album_item in album.items(): - file_chmod_queue.append(album_item.path) - - # A set of directories to change permissions for. - dir_chmod_queue = set() - - for path in file_chmod_queue: + for path in files: # Changing permissions on the destination file. self._log.debug( - u'setting file permissions on {}', + 'setting file permissions on {}', util.displayable_path(path), ) os.chmod(util.syspath(path), file_perm) @@ -105,16 +108,11 @@ class Permissions(BeetsPlugin): # Checks if the destination path has the permissions configured. assert_permissions(path, file_perm, self._log) - # Adding directories to the directory chmod queue. - dir_chmod_queue.update( - dirs_in_library(lib.directory, - path)) - # Change permissions for the directories. - for path in dir_chmod_queue: - # Chaning permissions on the destination directory. + for path in dirs: + # Changing permissions on the destination directory. self._log.debug( - u'setting directory permissions on {}', + 'setting directory permissions on {}', util.displayable_path(path), ) os.chmod(util.syspath(path), dir_perm) diff --git a/libs/common/beetsplug/play.py b/libs/common/beetsplug/play.py index 4d32a357..f4233490 100644 --- a/libs/common/beetsplug/play.py +++ b/libs/common/beetsplug/play.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, David Hamp-Gonsalves # @@ -15,7 +14,6 @@ """Send the results of a query to the configured music player as a playlist. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets.ui import Subcommand @@ -26,6 +24,7 @@ from beets import util from os.path import relpath from tempfile import NamedTemporaryFile import subprocess +import shlex # Indicate where arguments should be inserted into the command string. # If this is missing, they're placed at the end. @@ -39,25 +38,25 @@ def play(command_str, selection, paths, open_args, log, item_type='track', """ # Print number of tracks or albums to be played, log command to be run. item_type += 's' if len(selection) > 1 else '' - ui.print_(u'Playing {0} {1}.'.format(len(selection), item_type)) - log.debug(u'executing command: {} {!r}', command_str, open_args) + ui.print_('Playing {} {}.'.format(len(selection), item_type)) + log.debug('executing command: {} {!r}', command_str, open_args) try: if keep_open: - command = util.shlex_split(command_str) + command = shlex.split(command_str) command = command + open_args subprocess.call(command) else: util.interactive_open(open_args, command_str) except OSError as exc: raise ui.UserError( - "Could not play the query: {0}".format(exc)) + f"Could not play the query: {exc}") class PlayPlugin(BeetsPlugin): def __init__(self): - super(PlayPlugin, self).__init__() + super().__init__() config['play'].add({ 'command': None, @@ -65,6 +64,7 @@ class PlayPlugin(BeetsPlugin): 'relative_to': None, 'raw': False, 'warning_threshold': 100, + 'bom': False, }) self.register_listener('before_choose_candidate', @@ -73,18 +73,18 @@ class PlayPlugin(BeetsPlugin): def commands(self): play_command = Subcommand( 'play', - help=u'send music to a player as a playlist' + help='send music to a player as a playlist' ) play_command.parser.add_album_option() play_command.parser.add_option( - u'-A', u'--args', + '-A', '--args', action='store', - help=u'add additional arguments to the command', + help='add additional arguments to the command', ) play_command.parser.add_option( - u'-y', u'--yes', + '-y', '--yes', action="store_true", - help=u'skip the warning threshold', + help='skip the warning threshold', ) play_command.func = self._play_command return [play_command] @@ -123,7 +123,7 @@ class PlayPlugin(BeetsPlugin): if not selection: ui.print_(ui.colorize('text_warning', - u'No {0} to play.'.format(item_type))) + f'No {item_type} to play.')) return open_args = self._playlist_or_paths(paths) @@ -147,7 +147,7 @@ class PlayPlugin(BeetsPlugin): if ARGS_MARKER in command_str: return command_str.replace(ARGS_MARKER, args) else: - return u"{} {}".format(command_str, args) + return f"{command_str} {args}" else: # Don't include the marker in the command. return command_str.replace(" " + ARGS_MARKER, "") @@ -174,10 +174,10 @@ class PlayPlugin(BeetsPlugin): ui.print_(ui.colorize( 'text_warning', - u'You are about to queue {0} {1}.'.format( + 'You are about to queue {} {}.'.format( len(selection), item_type))) - if ui.input_options((u'Continue', u'Abort')) == 'a': + if ui.input_options(('Continue', 'Abort')) == 'a': return True return False @@ -185,7 +185,12 @@ class PlayPlugin(BeetsPlugin): def _create_tmp_playlist(self, paths_list): """Create a temporary .m3u file. Return the filename. """ + utf8_bom = config['play']['bom'].get(bool) m3u = NamedTemporaryFile('wb', suffix='.m3u', delete=False) + + if utf8_bom: + m3u.write(b'\xEF\xBB\xBF') + for item in paths_list: m3u.write(item + b'\n') m3u.close() diff --git a/libs/common/beetsplug/playlist.py b/libs/common/beetsplug/playlist.py new file mode 100644 index 00000000..265b8bad --- /dev/null +++ b/libs/common/beetsplug/playlist.py @@ -0,0 +1,185 @@ +# This file is part of beets. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + +import os +import fnmatch +import tempfile +import beets +from beets.util import path_as_posix + + +class PlaylistQuery(beets.dbcore.Query): + """Matches files listed by a playlist file. + """ + def __init__(self, pattern): + self.pattern = pattern + config = beets.config['playlist'] + + # Get the full path to the playlist + playlist_paths = ( + pattern, + os.path.abspath(os.path.join( + config['playlist_dir'].as_filename(), + f'{pattern}.m3u', + )), + ) + + self.paths = [] + for playlist_path in playlist_paths: + if not fnmatch.fnmatch(playlist_path, '*.[mM]3[uU]'): + # This is not am M3U playlist, skip this candidate + continue + + try: + f = open(beets.util.syspath(playlist_path), mode='rb') + except OSError: + continue + + if config['relative_to'].get() == 'library': + relative_to = beets.config['directory'].as_filename() + elif config['relative_to'].get() == 'playlist': + relative_to = os.path.dirname(playlist_path) + else: + relative_to = config['relative_to'].as_filename() + relative_to = beets.util.bytestring_path(relative_to) + + for line in f: + if line[0] == '#': + # ignore comments, and extm3u extension + continue + + self.paths.append(beets.util.normpath( + os.path.join(relative_to, line.rstrip()) + )) + f.close() + break + + def col_clause(self): + if not self.paths: + # Playlist is empty + return '0', () + clause = 'path IN ({})'.format(', '.join('?' for path in self.paths)) + return clause, (beets.library.BLOB_TYPE(p) for p in self.paths) + + def match(self, item): + return item.path in self.paths + + +class PlaylistPlugin(beets.plugins.BeetsPlugin): + item_queries = {'playlist': PlaylistQuery} + + def __init__(self): + super().__init__() + self.config.add({ + 'auto': False, + 'playlist_dir': '.', + 'relative_to': 'library', + 'forward_slash': False, + }) + + self.playlist_dir = self.config['playlist_dir'].as_filename() + self.changes = {} + + if self.config['relative_to'].get() == 'library': + self.relative_to = beets.util.bytestring_path( + beets.config['directory'].as_filename()) + elif self.config['relative_to'].get() != 'playlist': + self.relative_to = beets.util.bytestring_path( + self.config['relative_to'].as_filename()) + else: + self.relative_to = None + + if self.config['auto']: + self.register_listener('item_moved', self.item_moved) + self.register_listener('item_removed', self.item_removed) + self.register_listener('cli_exit', self.cli_exit) + + def item_moved(self, item, source, destination): + self.changes[source] = destination + + def item_removed(self, item): + if not os.path.exists(beets.util.syspath(item.path)): + self.changes[item.path] = None + + def cli_exit(self, lib): + for playlist in self.find_playlists(): + self._log.info(f'Updating playlist: {playlist}') + base_dir = beets.util.bytestring_path( + self.relative_to if self.relative_to + else os.path.dirname(playlist) + ) + + try: + self.update_playlist(playlist, base_dir) + except beets.util.FilesystemError: + self._log.error('Failed to update playlist: {}'.format( + beets.util.displayable_path(playlist))) + + def find_playlists(self): + """Find M3U playlists in the playlist directory.""" + try: + dir_contents = os.listdir(beets.util.syspath(self.playlist_dir)) + except OSError: + self._log.warning('Unable to open playlist directory {}'.format( + beets.util.displayable_path(self.playlist_dir))) + return + + for filename in dir_contents: + if fnmatch.fnmatch(filename, '*.[mM]3[uU]'): + yield os.path.join(self.playlist_dir, filename) + + def update_playlist(self, filename, base_dir): + """Find M3U playlists in the specified directory.""" + changes = 0 + deletions = 0 + + with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tempfp: + new_playlist = tempfp.name + with open(filename, mode='rb') as fp: + for line in fp: + original_path = line.rstrip(b'\r\n') + + # Ensure that path from playlist is absolute + is_relative = not os.path.isabs(line) + if is_relative: + lookup = os.path.join(base_dir, original_path) + else: + lookup = original_path + + try: + new_path = self.changes[beets.util.normpath(lookup)] + except KeyError: + if self.config['forward_slash']: + line = path_as_posix(line) + tempfp.write(line) + else: + if new_path is None: + # Item has been deleted + deletions += 1 + continue + + changes += 1 + if is_relative: + new_path = os.path.relpath(new_path, base_dir) + line = line.replace(original_path, new_path) + if self.config['forward_slash']: + line = path_as_posix(line) + tempfp.write(line) + + if changes or deletions: + self._log.info( + 'Updated playlist {} ({} changes, {} deletions)'.format( + filename, changes, deletions)) + beets.util.copy(new_playlist, filename, replace=True) + beets.util.remove(new_playlist) diff --git a/libs/common/beetsplug/plexupdate.py b/libs/common/beetsplug/plexupdate.py index 17fd8208..2261a55f 100644 --- a/libs/common/beetsplug/plexupdate.py +++ b/libs/common/beetsplug/plexupdate.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """Updates an Plex library whenever the beets library is changed. Plex Home users enter the Plex Token to enable updating. @@ -9,42 +7,51 @@ Put something like the following in your config.yaml to configure: port: 32400 token: token """ -from __future__ import division, absolute_import, print_function import requests -import xml.etree.ElementTree as ET -from six.moves.urllib.parse import urljoin, urlencode +from xml.etree import ElementTree +from urllib.parse import urljoin, urlencode from beets import config from beets.plugins import BeetsPlugin -def get_music_section(host, port, token, library_name): +def get_music_section(host, port, token, library_name, secure, + ignore_cert_errors): """Getting the section key for the music library in Plex. """ api_endpoint = append_token('library/sections', token) - url = urljoin('http://{0}:{1}'.format(host, port), api_endpoint) + url = urljoin('{}://{}:{}'.format(get_protocol(secure), host, + port), api_endpoint) # Sends request. - r = requests.get(url) + r = requests.get(url, verify=not ignore_cert_errors) # Parse xml tree and extract music section key. - tree = ET.fromstring(r.content) + tree = ElementTree.fromstring(r.content) for child in tree.findall('Directory'): if child.get('title') == library_name: return child.get('key') -def update_plex(host, port, token, library_name): +def update_plex(host, port, token, library_name, secure, + ignore_cert_errors): + """Ignore certificate errors if configured to. + """ + if ignore_cert_errors: + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) """Sends request to the Plex api to start a library refresh. """ # Getting section key and build url. - section_key = get_music_section(host, port, token, library_name) - api_endpoint = 'library/sections/{0}/refresh'.format(section_key) + section_key = get_music_section(host, port, token, library_name, + secure, ignore_cert_errors) + api_endpoint = f'library/sections/{section_key}/refresh' api_endpoint = append_token(api_endpoint, token) - url = urljoin('http://{0}:{1}'.format(host, port), api_endpoint) + url = urljoin('{}://{}:{}'.format(get_protocol(secure), host, + port), api_endpoint) # Sends request and returns requests object. - r = requests.get(url) + r = requests.get(url, verify=not ignore_cert_errors) return r @@ -56,16 +63,25 @@ def append_token(url, token): return url +def get_protocol(secure): + if secure: + return 'https' + else: + return 'http' + + class PlexUpdate(BeetsPlugin): def __init__(self): - super(PlexUpdate, self).__init__() + super().__init__() # Adding defaults. config['plex'].add({ - u'host': u'localhost', - u'port': 32400, - u'token': u'', - u'library_name': u'Music'}) + 'host': 'localhost', + 'port': 32400, + 'token': '', + 'library_name': 'Music', + 'secure': False, + 'ignore_cert_errors': False}) config['plex']['token'].redact = True self.register_listener('database_change', self.listen_for_db_change) @@ -77,7 +93,7 @@ class PlexUpdate(BeetsPlugin): def update(self, lib): """When the client exists try to send refresh request to Plex server. """ - self._log.info(u'Updating Plex library...') + self._log.info('Updating Plex library...') # Try to send update request. try: @@ -85,8 +101,10 @@ class PlexUpdate(BeetsPlugin): config['plex']['host'].get(), config['plex']['port'].get(), config['plex']['token'].get(), - config['plex']['library_name'].get()) - self._log.info(u'... started.') + config['plex']['library_name'].get(), + config['plex']['secure'].get(bool), + config['plex']['ignore_cert_errors'].get(bool)) + self._log.info('... started.') except requests.exceptions.RequestException: - self._log.warning(u'Update failed.') + self._log.warning('Update failed.') diff --git a/libs/common/beetsplug/random.py b/libs/common/beetsplug/random.py index 65caaf90..ea9b7b98 100644 --- a/libs/common/beetsplug/random.py +++ b/libs/common/beetsplug/random.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Philippe Mongeau. # @@ -15,101 +14,10 @@ """Get a random song or album from the library. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets.ui import Subcommand, decargs, print_ -import random -from operator import attrgetter -from itertools import groupby - - -def _length(obj, album): - """Get the duration of an item or album. - """ - if album: - return sum(i.length for i in obj.items()) - else: - return obj.length - - -def _equal_chance_permutation(objs, field='albumartist'): - """Generate (lazily) a permutation of the objects where every group - with equal values for `field` have an equal chance of appearing in - any given position. - """ - # Group the objects by artist so we can sample from them. - key = attrgetter(field) - objs.sort(key=key) - objs_by_artists = {} - for artist, v in groupby(objs, key): - objs_by_artists[artist] = list(v) - - # While we still have artists with music to choose from, pick one - # randomly and pick a track from that artist. - while objs_by_artists: - # Choose an artist and an object for that artist, removing - # this choice from the pool. - artist = random.choice(list(objs_by_artists.keys())) - objs_from_artist = objs_by_artists[artist] - i = random.randint(0, len(objs_from_artist) - 1) - yield objs_from_artist.pop(i) - - # Remove the artist if we've used up all of its objects. - if not objs_from_artist: - del objs_by_artists[artist] - - -def _take(iter, num): - """Return a list containing the first `num` values in `iter` (or - fewer, if the iterable ends early). - """ - out = [] - for val in iter: - out.append(val) - num -= 1 - if num <= 0: - break - return out - - -def _take_time(iter, secs, album): - """Return a list containing the first values in `iter`, which should - be Item or Album objects, that add up to the given amount of time in - seconds. - """ - out = [] - total_time = 0.0 - for obj in iter: - length = _length(obj, album) - if total_time + length <= secs: - out.append(obj) - total_time += length - return out - - -def random_objs(objs, album, number=1, time=None, equal_chance=False): - """Get a random subset of the provided `objs`. - - If `number` is provided, produce that many matches. Otherwise, if - `time` is provided, instead select a list whose total time is close - to that number of minutes. If `equal_chance` is true, give each - artist an equal chance of being included so that artists with more - songs are not represented disproportionately. - """ - # Permute the objects either in a straightforward way or an - # artist-balanced way. - if equal_chance: - perm = _equal_chance_permutation(objs) - else: - perm = objs - random.shuffle(perm) # N.B. This shuffles the original list. - - # Select objects by time our count. - if time: - return _take_time(perm, time * 60, album) - else: - return _take(perm, number) +from beets.random import random_objs def random_func(lib, opts, args): @@ -130,16 +38,16 @@ def random_func(lib, opts, args): random_cmd = Subcommand('random', - help=u'choose a random track or album') + help='choose a random track or album') random_cmd.parser.add_option( - u'-n', u'--number', action='store', type="int", - help=u'number of objects to choose', default=1) + '-n', '--number', action='store', type="int", + help='number of objects to choose', default=1) random_cmd.parser.add_option( - u'-e', u'--equal-chance', action='store_true', - help=u'each artist has the same chance') + '-e', '--equal-chance', action='store_true', + help='each artist has the same chance') random_cmd.parser.add_option( - u'-t', u'--time', action='store', type="float", - help=u'total length in minutes of objects to choose') + '-t', '--time', action='store', type="float", + help='total length in minutes of objects to choose') random_cmd.parser.add_all_common_options() random_cmd.func = random_func diff --git a/libs/common/beetsplug/replaygain.py b/libs/common/beetsplug/replaygain.py index a7eb81b5..b6297d93 100644 --- a/libs/common/beetsplug/replaygain.py +++ b/libs/common/beetsplug/replaygain.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Fabrice Laporte, Yevgeny Bezman, and Adrian Sampson. # @@ -13,20 +12,23 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function -import subprocess -import os import collections +import enum +import math +import os +import signal +import subprocess import sys import warnings -import xml.parsers.expat -from six.moves import zip +from multiprocessing.pool import ThreadPool, RUN +from six.moves import queue +from threading import Thread, Event from beets import ui from beets.plugins import BeetsPlugin -from beets.util import (syspath, command_output, bytestring_path, - displayable_path, py3_path) +from beets.util import (syspath, command_output, displayable_path, + py3_path, cpu_count) # Utilities. @@ -47,238 +49,342 @@ class FatalGstreamerPluginReplayGainError(FatalReplayGainError): loading the required plugins.""" -def call(args): +def call(args, **kwargs): """Execute the command and return its output or raise a ReplayGainError on failure. """ try: - return command_output(args) + return command_output(args, **kwargs) except subprocess.CalledProcessError as e: raise ReplayGainError( - u"{0} exited with status {1}".format(args[0], e.returncode) + "{} exited with status {}".format(args[0], e.returncode) ) except UnicodeEncodeError: # Due to a bug in Python 2's subprocess on Windows, Unicode # filenames can fail to encode on that platform. See: # https://github.com/google-code-export/beets/issues/499 - raise ReplayGainError(u"argument encoding failed") + raise ReplayGainError("argument encoding failed") + + +def after_version(version_a, version_b): + return tuple(int(s) for s in version_a.split('.')) \ + >= tuple(int(s) for s in version_b.split('.')) + + +def db_to_lufs(db): + """Convert db to LUFS. + + According to https://wiki.hydrogenaud.io/index.php?title= + ReplayGain_2.0_specification#Reference_level + """ + return db - 107 + + +def lufs_to_db(db): + """Convert LUFS to db. + + According to https://wiki.hydrogenaud.io/index.php?title= + ReplayGain_2.0_specification#Reference_level + """ + return db + 107 # Backend base and plumbing classes. +# gain: in LU to reference level +# peak: part of full scale (FS is 1.0) Gain = collections.namedtuple("Gain", "gain peak") +# album_gain: Gain object +# track_gains: list of Gain objects AlbumGain = collections.namedtuple("AlbumGain", "album_gain track_gains") -class Backend(object): +class Peak(enum.Enum): + none = 0 + true = 1 + sample = 2 + + +class Backend: """An abstract class representing engine for calculating RG values. """ + do_parallel = False + def __init__(self, config, log): """Initialize the backend with the configuration view for the plugin. """ self._log = log - def compute_track_gain(self, items): - raise NotImplementedError() - - def compute_album_gain(self, album): - # TODO: implement album gain in terms of track gain of the - # individual tracks which can be used for any backend. - raise NotImplementedError() - - -# bsg1770gain backend -class Bs1770gainBackend(Backend): - """bs1770gain is a loudness scanner compliant with ITU-R BS.1770 and - its flavors EBU R128, ATSC A/85 and Replaygain 2.0. - """ - - def __init__(self, config, log): - super(Bs1770gainBackend, self).__init__(config, log) - config.add({ - 'chunk_at': 5000, - 'method': 'replaygain', - }) - self.chunk_at = config['chunk_at'].as_number() - self.method = '--' + config['method'].as_str() - - cmd = 'bs1770gain' - try: - call([cmd, self.method]) - self.command = cmd - except OSError: - raise FatalReplayGainError( - u'Is bs1770gain installed? Is your method in config correct?' - ) - if not self.command: - raise FatalReplayGainError( - u'no replaygain command found: install bs1770gain' - ) - - def compute_track_gain(self, items): + def compute_track_gain(self, items, target_level, peak): """Computes the track gain of the given tracks, returns a list - of TrackGain objects. + of Gain objects. """ + raise NotImplementedError() - output = self.compute_gain(items, False) - return output - - def compute_album_gain(self, album): + def compute_album_gain(self, items, target_level, peak): """Computes the album gain of the given album, returns an AlbumGain object. """ - # TODO: What should be done when not all tracks in the album are - # supported? + raise NotImplementedError() - supported_items = album.items() - output = self.compute_gain(supported_items, True) - if not output: - raise ReplayGainError(u'no output from bs1770gain') - return AlbumGain(output[-1], output[:-1]) +# ffmpeg backend +class FfmpegBackend(Backend): + """A replaygain backend using ffmpeg's ebur128 filter. + """ - def isplitter(self, items, chunk_at): - """Break an iterable into chunks of at most size `chunk_at`, - generating lists for each chunk. - """ - iterable = iter(items) - while True: - result = [] - for i in range(chunk_at): - try: - a = next(iterable) - except StopIteration: - break - else: - result.append(a) - if result: - yield result - else: - break + do_parallel = True - def compute_gain(self, items, is_album): - """Computes the track or album gain of a list of items, returns - a list of TrackGain objects. - When computing album gain, the last TrackGain object returned is - the album gain - """ + def __init__(self, config, log): + super().__init__(config, log) + self._ffmpeg_path = "ffmpeg" - if len(items) == 0: - return [] - - albumgaintot = 0.0 - albumpeaktot = 0.0 - returnchunks = [] - - # In the case of very large sets of music, we break the tracks - # into smaller chunks and process them one at a time. This - # avoids running out of memory. - if len(items) > self.chunk_at: - i = 0 - for chunk in self.isplitter(items, self.chunk_at): - i += 1 - returnchunk = self.compute_chunk_gain(chunk, is_album) - albumgaintot += returnchunk[-1].gain - albumpeaktot += returnchunk[-1].peak - returnchunks = returnchunks + returnchunk[0:-1] - returnchunks.append(Gain(albumgaintot / i, albumpeaktot / i)) - return returnchunks - else: - return self.compute_chunk_gain(items, is_album) - - def compute_chunk_gain(self, items, is_album): - """Compute ReplayGain values and return a list of results - dictionaries as given by `parse_tool_output`. - """ - # Construct shell command. - cmd = [self.command] - cmd += [self.method] - cmd += ['--xml', '-p'] - - # Workaround for Windows: the underlying tool fails on paths - # with the \\?\ prefix, so we don't use it here. This - # prevents the backend from working with long paths. - args = cmd + [syspath(i.path, prefix=False) for i in items] - path_list = [i.path for i in items] - - # Invoke the command. - self._log.debug( - u'executing {0}', u' '.join(map(displayable_path, args)) - ) - output = call(args) - - self._log.debug(u'analysis finished: {0}', output) - results = self.parse_tool_output(output, path_list, is_album) - self._log.debug(u'{0} items, {1} results', len(items), len(results)) - return results - - def parse_tool_output(self, text, path_list, is_album): - """Given the output from bs1770gain, parse the text and - return a list of dictionaries - containing information about each analyzed file. - """ - per_file_gain = {} - album_gain = {} # mutable variable so it can be set from handlers - parser = xml.parsers.expat.ParserCreate(encoding='utf-8') - state = {'file': None, 'gain': None, 'peak': None} - - def start_element_handler(name, attrs): - if name == u'track': - state['file'] = bytestring_path(attrs[u'file']) - if state['file'] in per_file_gain: - raise ReplayGainError( - u'duplicate filename in bs1770gain output') - elif name == u'integrated': - state['gain'] = float(attrs[u'lu']) - elif name == u'sample-peak': - state['peak'] = float(attrs[u'factor']) - - def end_element_handler(name): - if name == u'track': - if state['gain'] is None or state['peak'] is None: - raise ReplayGainError(u'could not parse gain or peak from ' - 'the output of bs1770gain') - per_file_gain[state['file']] = Gain(state['gain'], - state['peak']) - state['gain'] = state['peak'] = None - elif name == u'summary': - if state['gain'] is None or state['peak'] is None: - raise ReplayGainError(u'could not parse gain or peak from ' - 'the output of bs1770gain') - album_gain["album"] = Gain(state['gain'], state['peak']) - state['gain'] = state['peak'] = None - parser.StartElementHandler = start_element_handler - parser.EndElementHandler = end_element_handler - parser.Parse(text, True) - - if len(per_file_gain) != len(path_list): - raise ReplayGainError( - u'the number of results returned by bs1770gain does not match ' - 'the number of files passed to it') - - # bs1770gain does not return the analysis results in the order that - # files are passed on the command line, because it is sorting the files - # internally. We must recover the order from the filenames themselves. + # check that ffmpeg is installed try: - out = [per_file_gain[os.path.basename(p)] for p in path_list] - except KeyError: + ffmpeg_version_out = call([self._ffmpeg_path, "-version"]) + except OSError: + raise FatalReplayGainError( + f"could not find ffmpeg at {self._ffmpeg_path}" + ) + incompatible_ffmpeg = True + for line in ffmpeg_version_out.stdout.splitlines(): + if line.startswith(b"configuration:"): + if b"--enable-libebur128" in line: + incompatible_ffmpeg = False + if line.startswith(b"libavfilter"): + version = line.split(b" ", 1)[1].split(b"/", 1)[0].split(b".") + version = tuple(map(int, version)) + if version >= (6, 67, 100): + incompatible_ffmpeg = False + if incompatible_ffmpeg: + raise FatalReplayGainError( + "Installed FFmpeg version does not support ReplayGain." + "calculation. Either libavfilter version 6.67.100 or above or" + "the --enable-libebur128 configuration option is required." + ) + + def compute_track_gain(self, items, target_level, peak): + """Computes the track gain of the given tracks, returns a list + of Gain objects (the track gains). + """ + gains = [] + for item in items: + gains.append( + self._analyse_item( + item, + target_level, + peak, + count_blocks=False, + )[0] # take only the gain, discarding number of gating blocks + ) + return gains + + def compute_album_gain(self, items, target_level, peak): + """Computes the album gain of the given album, returns an + AlbumGain object. + """ + target_level_lufs = db_to_lufs(target_level) + + # analyse tracks + # list of track Gain objects + track_gains = [] + # maximum peak + album_peak = 0 + # sum of BS.1770 gating block powers + sum_powers = 0 + # total number of BS.1770 gating blocks + n_blocks = 0 + + for item in items: + track_gain, track_n_blocks = self._analyse_item( + item, target_level, peak + ) + track_gains.append(track_gain) + + # album peak is maximum track peak + album_peak = max(album_peak, track_gain.peak) + + # prepare album_gain calculation + # total number of blocks is sum of track blocks + n_blocks += track_n_blocks + + # convert `LU to target_level` -> LUFS + track_loudness = target_level_lufs - track_gain.gain + # This reverses ITU-R BS.1770-4 p. 6 equation (5) to convert + # from loudness to power. The result is the average gating + # block power. + track_power = 10**((track_loudness + 0.691) / 10) + + # Weight that average power by the number of gating blocks to + # get the sum of all their powers. Add that to the sum of all + # block powers in this album. + sum_powers += track_power * track_n_blocks + + # calculate album gain + if n_blocks > 0: + # compare ITU-R BS.1770-4 p. 6 equation (5) + # Album gain is the replaygain of the concatenation of all tracks. + album_gain = -0.691 + 10 * math.log10(sum_powers / n_blocks) + else: + album_gain = -70 + # convert LUFS -> `LU to target_level` + album_gain = target_level_lufs - album_gain + + self._log.debug( + "{}: gain {} LU, peak {}" + .format(items, album_gain, album_peak) + ) + + return AlbumGain(Gain(album_gain, album_peak), track_gains) + + def _construct_cmd(self, item, peak_method): + """Construct the shell command to analyse items.""" + return [ + self._ffmpeg_path, + "-nostats", + "-hide_banner", + "-i", + item.path, + "-map", + "a:0", + "-filter", + f"ebur128=peak={peak_method}", + "-f", + "null", + "-", + ] + + def _analyse_item(self, item, target_level, peak, count_blocks=True): + """Analyse item. Return a pair of a Gain object and the number + of gating blocks above the threshold. + + If `count_blocks` is False, the number of gating blocks returned + will be 0. + """ + target_level_lufs = db_to_lufs(target_level) + peak_method = peak.name + + # call ffmpeg + self._log.debug(f"analyzing {item}") + cmd = self._construct_cmd(item, peak_method) + self._log.debug( + 'executing {0}', ' '.join(map(displayable_path, cmd)) + ) + output = call(cmd).stderr.splitlines() + + # parse output + + if peak == Peak.none: + peak = 0 + else: + line_peak = self._find_line( + output, + f" {peak_method.capitalize()} peak:".encode(), + start_line=len(output) - 1, step_size=-1, + ) + peak = self._parse_float( + output[self._find_line( + output, b" Peak:", + line_peak, + )] + ) + # convert TPFS -> part of FS + peak = 10**(peak / 20) + + line_integrated_loudness = self._find_line( + output, b" Integrated loudness:", + start_line=len(output) - 1, step_size=-1, + ) + gain = self._parse_float( + output[self._find_line( + output, b" I:", + line_integrated_loudness, + )] + ) + # convert LUFS -> LU from target level + gain = target_level_lufs - gain + + # count BS.1770 gating blocks + n_blocks = 0 + if count_blocks: + gating_threshold = self._parse_float( + output[self._find_line( + output, b" Threshold:", + start_line=line_integrated_loudness, + )] + ) + for line in output: + if not line.startswith(b"[Parsed_ebur128"): + continue + if line.endswith(b"Summary:"): + continue + line = line.split(b"M:", 1) + if len(line) < 2: + continue + if self._parse_float(b"M: " + line[1]) >= gating_threshold: + n_blocks += 1 + self._log.debug( + "{}: {} blocks over {} LUFS" + .format(item, n_blocks, gating_threshold) + ) + + self._log.debug( + "{}: gain {} LU, peak {}" + .format(item, gain, peak) + ) + + return Gain(gain, peak), n_blocks + + def _find_line(self, output, search, start_line=0, step_size=1): + """Return index of line beginning with `search`. + + Begins searching at index `start_line` in `output`. + """ + end_index = len(output) if step_size > 0 else -1 + for i in range(start_line, end_index, step_size): + if output[i].startswith(search): + return i + raise ReplayGainError( + "ffmpeg output: missing {} after line {}" + .format(repr(search), start_line) + ) + + def _parse_float(self, line): + """Extract a float from a key value pair in `line`. + + This format is expected: /[^:]:[[:space:]]*value.*/, where `value` is + the float. + """ + # extract value + value = line.split(b":", 1) + if len(value) < 2: raise ReplayGainError( - u'unrecognized filename in bs1770gain output ' - '(bs1770gain can only deal with utf-8 file names)') - if is_album: - out.append(album_gain["album"]) - return out + "ffmpeg output: expected key value pair, found {}" + .format(line) + ) + value = value[1].lstrip() + # strip unit + value = value.split(b" ", 1)[0] + # cast value to float + try: + return float(value) + except ValueError: + raise ReplayGainError( + "ffmpeg output: expected float value, found {}" + .format(value) + ) # mpgain/aacgain CLI tool backend. class CommandBackend(Backend): + do_parallel = True def __init__(self, config, log): - super(CommandBackend, self).__init__(config, log) + super().__init__(config, log) config.add({ - 'command': u"", + 'command': "", 'noclip': True, }) @@ -288,7 +394,7 @@ class CommandBackend(Backend): # Explicit executable path. if not os.path.isfile(self.command): raise FatalReplayGainError( - u'replaygain command does not exist: {0}'.format( + 'replaygain command does not exist: {}'.format( self.command) ) else: @@ -301,34 +407,32 @@ class CommandBackend(Backend): pass if not self.command: raise FatalReplayGainError( - u'no replaygain command found: install mp3gain or aacgain' + 'no replaygain command found: install mp3gain or aacgain' ) self.noclip = config['noclip'].get(bool) - target_level = config['targetlevel'].as_number() - self.gain_offset = int(target_level - 89) - def compute_track_gain(self, items): + def compute_track_gain(self, items, target_level, peak): """Computes the track gain of the given tracks, returns a list of TrackGain objects. """ supported_items = list(filter(self.format_supported, items)) - output = self.compute_gain(supported_items, False) + output = self.compute_gain(supported_items, target_level, False) return output - def compute_album_gain(self, album): + def compute_album_gain(self, items, target_level, peak): """Computes the album gain of the given album, returns an AlbumGain object. """ # TODO: What should be done when not all tracks in the album are # supported? - supported_items = list(filter(self.format_supported, album.items())) - if len(supported_items) != len(album.items()): - self._log.debug(u'tracks are of unsupported format') + supported_items = list(filter(self.format_supported, items)) + if len(supported_items) != len(items): + self._log.debug('tracks are of unsupported format') return AlbumGain(None, []) - output = self.compute_gain(supported_items, True) + output = self.compute_gain(supported_items, target_level, True) return AlbumGain(output[-1], output[:-1]) def format_supported(self, item): @@ -340,7 +444,7 @@ class CommandBackend(Backend): return False return True - def compute_gain(self, items, is_album): + def compute_gain(self, items, target_level, is_album): """Computes the track or album gain of a list of items, returns a list of TrackGain objects. @@ -348,7 +452,7 @@ class CommandBackend(Backend): the album gain """ if len(items) == 0: - self._log.debug(u'no supported tracks to analyze') + self._log.debug('no supported tracks to analyze') return [] """Compute ReplayGain values and return a list of results @@ -367,13 +471,13 @@ class CommandBackend(Backend): else: # Disable clipping warning. cmd = cmd + ['-c'] - cmd = cmd + ['-d', str(self.gain_offset)] + cmd = cmd + ['-d', str(int(target_level - 89))] cmd = cmd + [syspath(i.path) for i in items] - self._log.debug(u'analyzing {0} files', len(items)) - self._log.debug(u"executing {0}", " ".join(map(displayable_path, cmd))) - output = call(cmd) - self._log.debug(u'analysis finished') + self._log.debug('analyzing {0} files', len(items)) + self._log.debug("executing {0}", " ".join(map(displayable_path, cmd))) + output = call(cmd).stdout + self._log.debug('analysis finished') return self.parse_tool_output(output, len(items) + (1 if is_album else 0)) @@ -386,8 +490,8 @@ class CommandBackend(Backend): for line in text.split(b'\n')[1:num_lines + 1]: parts = line.split(b'\t') if len(parts) != 6 or parts[0] == b'File': - self._log.debug(u'bad tool output: {0}', text) - raise ReplayGainError(u'mp3gain failed') + self._log.debug('bad tool output: {0}', text) + raise ReplayGainError('mp3gain failed') d = { 'file': parts[0], 'mp3gain': int(parts[1]), @@ -404,9 +508,8 @@ class CommandBackend(Backend): # GStreamer-based backend. class GStreamerBackend(Backend): - def __init__(self, config, log): - super(GStreamerBackend, self).__init__(config, log) + super().__init__(config, log) self._import_gst() # Initialized a GStreamer pipeline of the form filesrc -> @@ -423,15 +526,13 @@ class GStreamerBackend(Backend): if self._src is None or self._decbin is None or self._conv is None \ or self._res is None or self._rg is None: raise FatalGstreamerPluginReplayGainError( - u"Failed to load required GStreamer plugins" + "Failed to load required GStreamer plugins" ) # We check which files need gain ourselves, so all files given # to rganalsys should have their gain computed, even if it # already exists. self._rg.set_property("forced", True) - self._rg.set_property("reference-level", - config["targetlevel"].as_number()) self._sink = self.Gst.ElementFactory.make("fakesink", "sink") self._pipe = self.Gst.Pipeline() @@ -470,14 +571,14 @@ class GStreamerBackend(Backend): import gi except ImportError: raise FatalReplayGainError( - u"Failed to load GStreamer: python-gi not found" + "Failed to load GStreamer: python-gi not found" ) try: gi.require_version('Gst', '1.0') except ValueError as e: raise FatalReplayGainError( - u"Failed to load GStreamer 1.0: {0}".format(e) + f"Failed to load GStreamer 1.0: {e}" ) from gi.repository import GObject, Gst, GLib @@ -492,7 +593,7 @@ class GStreamerBackend(Backend): self.GLib = GLib self.Gst = Gst - def compute(self, files, album): + def compute(self, files, target_level, album): self._error = None self._files = list(files) @@ -501,6 +602,8 @@ class GStreamerBackend(Backend): self._file_tags = collections.defaultdict(dict) + self._rg.set_property("reference-level", target_level) + if album: self._rg.set_property("num-tracks", len(self._files)) @@ -509,10 +612,10 @@ class GStreamerBackend(Backend): if self._error is not None: raise self._error - def compute_track_gain(self, items): - self.compute(items, False) + def compute_track_gain(self, items, target_level, peak): + self.compute(items, target_level, False) if len(self._file_tags) != len(items): - raise ReplayGainError(u"Some tracks did not receive tags") + raise ReplayGainError("Some tracks did not receive tags") ret = [] for item in items: @@ -521,11 +624,11 @@ class GStreamerBackend(Backend): return ret - def compute_album_gain(self, album): - items = list(album.items()) - self.compute(items, True) + def compute_album_gain(self, items, target_level, peak): + items = list(items) + self.compute(items, target_level, True) if len(self._file_tags) != len(items): - raise ReplayGainError(u"Some items in album did not receive tags") + raise ReplayGainError("Some items in album did not receive tags") # Collect track gains. track_gains = [] @@ -534,7 +637,7 @@ class GStreamerBackend(Backend): gain = self._file_tags[item]["TRACK_GAIN"] peak = self._file_tags[item]["TRACK_PEAK"] except KeyError: - raise ReplayGainError(u"results missing for track") + raise ReplayGainError("results missing for track") track_gains.append(Gain(gain, peak)) # Get album gain information from the last track. @@ -543,7 +646,7 @@ class GStreamerBackend(Backend): gain = last_tags["ALBUM_GAIN"] peak = last_tags["ALBUM_PEAK"] except KeyError: - raise ReplayGainError(u"results missing for album") + raise ReplayGainError("results missing for album") return AlbumGain(Gain(gain, peak), track_gains) @@ -565,7 +668,7 @@ class GStreamerBackend(Backend): f = self._src.get_property("location") # A GStreamer error, either an unsupported format or a bug. self._error = ReplayGainError( - u"Error {0!r} - {1!r} on file {2!r}".format(err, debug, f) + f"Error {err!r} - {debug!r} on file {f!r}" ) def _on_tag(self, bus, message): @@ -678,7 +781,7 @@ class AudioToolsBackend(Backend): """ def __init__(self, config, log): - super(AudioToolsBackend, self).__init__(config, log) + super().__init__(config, log) self._import_audiotools() def _import_audiotools(self): @@ -692,7 +795,7 @@ class AudioToolsBackend(Backend): import audiotools.replaygain except ImportError: raise FatalReplayGainError( - u"Failed to load audiotools: audiotools not found" + "Failed to load audiotools: audiotools not found" ) self._mod_audiotools = audiotools self._mod_replaygain = audiotools.replaygain @@ -707,14 +810,14 @@ class AudioToolsBackend(Backend): file format is not supported """ try: - audiofile = self._mod_audiotools.open(item.path) - except IOError: + audiofile = self._mod_audiotools.open(py3_path(syspath(item.path))) + except OSError: raise ReplayGainError( - u"File {} was not found".format(item.path) + f"File {item.path} was not found" ) except self._mod_audiotools.UnsupportedFile: raise ReplayGainError( - u"Unsupported file type {}".format(item.format) + f"Unsupported file type {item.format}" ) return audiofile @@ -733,18 +836,25 @@ class AudioToolsBackend(Backend): rg = self._mod_replaygain.ReplayGain(audiofile.sample_rate()) except ValueError: raise ReplayGainError( - u"Unsupported sample rate {}".format(item.samplerate)) + f"Unsupported sample rate {item.samplerate}") return return rg - def compute_track_gain(self, items): + def compute_track_gain(self, items, target_level, peak): """Compute ReplayGain values for the requested items. :return list: list of :class:`Gain` objects """ - return [self._compute_track_gain(item) for item in items] + return [self._compute_track_gain(item, target_level) for item in items] - def _title_gain(self, rg, audiofile): + def _with_target_level(self, gain, target_level): + """Return `gain` relative to `target_level`. + + Assumes `gain` is relative to 89 db. + """ + return gain + (target_level - 89) + + def _title_gain(self, rg, audiofile, target_level): """Get the gain result pair from PyAudioTools using the `ReplayGain` instance `rg` for the given `audiofile`. @@ -754,14 +864,15 @@ class AudioToolsBackend(Backend): try: # The method needs an audiotools.PCMReader instance that can # be obtained from an audiofile instance. - return rg.title_gain(audiofile.to_pcm()) + gain, peak = rg.title_gain(audiofile.to_pcm()) except ValueError as exc: # `audiotools.replaygain` can raise a `ValueError` if the sample # rate is incorrect. - self._log.debug(u'error in rg.title_gain() call: {}', exc) - raise ReplayGainError(u'audiotools audio data error') + self._log.debug('error in rg.title_gain() call: {}', exc) + raise ReplayGainError('audiotools audio data error') + return self._with_target_level(gain, target_level), peak - def _compute_track_gain(self, item): + def _compute_track_gain(self, item, target_level): """Compute ReplayGain value for the requested item. :rtype: :class:`Gain` @@ -771,41 +882,44 @@ class AudioToolsBackend(Backend): # Each call to title_gain on a ReplayGain object returns peak and gain # of the track. - rg_track_gain, rg_track_peak = self._title_gain(rg, audiofile) + rg_track_gain, rg_track_peak = self._title_gain( + rg, audiofile, target_level + ) - self._log.debug(u'ReplayGain for track {0} - {1}: {2:.2f}, {3:.2f}', + self._log.debug('ReplayGain for track {0} - {1}: {2:.2f}, {3:.2f}', item.artist, item.title, rg_track_gain, rg_track_peak) return Gain(gain=rg_track_gain, peak=rg_track_peak) - def compute_album_gain(self, album): + def compute_album_gain(self, items, target_level, peak): """Compute ReplayGain values for the requested album and its items. :rtype: :class:`AlbumGain` """ - self._log.debug(u'Analysing album {0}', album) - # The first item is taken and opened to get the sample rate to # initialize the replaygain object. The object is used for all the # tracks in the album to get the album values. - item = list(album.items())[0] + item = list(items)[0] audiofile = self.open_audio_file(item) rg = self.init_replaygain(audiofile, item) track_gains = [] - for item in album.items(): + for item in items: audiofile = self.open_audio_file(item) - rg_track_gain, rg_track_peak = self._title_gain(rg, audiofile) + rg_track_gain, rg_track_peak = self._title_gain( + rg, audiofile, target_level + ) track_gains.append( Gain(gain=rg_track_gain, peak=rg_track_peak) ) - self._log.debug(u'ReplayGain for track {0}: {1:.2f}, {2:.2f}', + self._log.debug('ReplayGain for track {0}: {1:.2f}, {2:.2f}', item, rg_track_gain, rg_track_peak) # After getting the values for all tracks, it's possible to get the # album values. rg_album_gain, rg_album_peak = rg.album_gain() - self._log.debug(u'ReplayGain for album {0}: {1:.2f}, {2:.2f}', - album, rg_album_gain, rg_album_peak) + rg_album_gain = self._with_target_level(rg_album_gain, target_level) + self._log.debug('ReplayGain for album {0}: {1:.2f}, {2:.2f}', + items[0].album, rg_album_gain, rg_album_peak) return AlbumGain( Gain(gain=rg_album_gain, peak=rg_album_peak), @@ -813,6 +927,33 @@ class AudioToolsBackend(Backend): ) +class ExceptionWatcher(Thread): + """Monitors a queue for exceptions asynchronously. + Once an exception occurs, raise it and execute a callback. + """ + + def __init__(self, queue, callback): + self._queue = queue + self._callback = callback + self._stopevent = Event() + Thread.__init__(self) + + def run(self): + while not self._stopevent.is_set(): + try: + exc = self._queue.get_nowait() + self._callback() + raise exc[1].with_traceback(exc[2]) + except queue.Empty: + # No exceptions yet, loop back to check + # whether `_stopevent` is set + pass + + def join(self, timeout=None): + self._stopevent.set() + Thread.join(self, timeout) + + # Main plugin logic. class ReplayGainPlugin(BeetsPlugin): @@ -823,48 +964,72 @@ class ReplayGainPlugin(BeetsPlugin): "command": CommandBackend, "gstreamer": GStreamerBackend, "audiotools": AudioToolsBackend, - "bs1770gain": Bs1770gainBackend, + "ffmpeg": FfmpegBackend, + } + + peak_methods = { + "true": Peak.true, + "sample": Peak.sample, } def __init__(self): - super(ReplayGainPlugin, self).__init__() + super().__init__() # default backend is 'command' for backward-compatibility. self.config.add({ 'overwrite': False, 'auto': True, - 'backend': u'command', + 'backend': 'command', + 'threads': cpu_count(), + 'parallel_on_import': False, + 'per_disc': False, + 'peak': 'true', 'targetlevel': 89, 'r128': ['Opus'], + 'r128_targetlevel': lufs_to_db(-23), }) self.overwrite = self.config['overwrite'].get(bool) - backend_name = self.config['backend'].as_str() - if backend_name not in self.backends: + self.per_disc = self.config['per_disc'].get(bool) + + # Remember which backend is used for CLI feedback + self.backend_name = self.config['backend'].as_str() + + if self.backend_name not in self.backends: raise ui.UserError( - u"Selected ReplayGain backend {0} is not supported. " - u"Please select one of: {1}".format( - backend_name, - u', '.join(self.backends.keys()) + "Selected ReplayGain backend {} is not supported. " + "Please select one of: {}".format( + self.backend_name, + ', '.join(self.backends.keys()) ) ) + peak_method = self.config["peak"].as_str() + if peak_method not in self.peak_methods: + raise ui.UserError( + "Selected ReplayGain peak method {} is not supported. " + "Please select one of: {}".format( + peak_method, + ', '.join(self.peak_methods.keys()) + ) + ) + self._peak_method = self.peak_methods[peak_method] # On-import analysis. if self.config['auto']: + self.register_listener('import_begin', self.import_begin) + self.register_listener('import', self.import_end) self.import_stages = [self.imported] # Formats to use R128. self.r128_whitelist = self.config['r128'].as_str_seq() try: - self.backend_instance = self.backends[backend_name]( + self.backend_instance = self.backends[self.backend_name]( self.config, self._log ) except (ReplayGainError, FatalReplayGainError) as e: raise ui.UserError( - u'replaygain initialization failed: {0}'.format(e)) - - self.r128_backend_instance = '' + f'replaygain initialization failed: {e}') def should_use_r128(self, item): """Checks the plugin setting to decide whether the calculation @@ -895,29 +1060,47 @@ class ReplayGainPlugin(BeetsPlugin): item.rg_track_gain = track_gain.gain item.rg_track_peak = track_gain.peak item.store() - - self._log.debug(u'applied track gain {0}, peak {1}', + self._log.debug('applied track gain {0} LU, peak {1} of FS', item.rg_track_gain, item.rg_track_peak) + def store_album_gain(self, item, album_gain): + item.rg_album_gain = album_gain.gain + item.rg_album_peak = album_gain.peak + item.store() + self._log.debug('applied album gain {0} LU, peak {1} of FS', + item.rg_album_gain, item.rg_album_peak) + def store_track_r128_gain(self, item, track_gain): - item.r128_track_gain = int(round(track_gain.gain * pow(2, 8))) + item.r128_track_gain = track_gain.gain item.store() - self._log.debug(u'applied track gain {0}', item.r128_track_gain) + self._log.debug('applied r128 track gain {0} LU', + item.r128_track_gain) - def store_album_gain(self, album, album_gain): - album.rg_album_gain = album_gain.gain - album.rg_album_peak = album_gain.peak - album.store() + def store_album_r128_gain(self, item, album_gain): + item.r128_album_gain = album_gain.gain + item.store() + self._log.debug('applied r128 album gain {0} LU', + item.r128_album_gain) - self._log.debug(u'applied album gain {0}, peak {1}', - album.rg_album_gain, album.rg_album_peak) + def tag_specific_values(self, items): + """Return some tag specific values. - def store_album_r128_gain(self, album, album_gain): - album.r128_album_gain = int(round(album_gain.gain * pow(2, 8))) - album.store() + Returns a tuple (store_track_gain, store_album_gain, target_level, + peak_method). + """ + if any([self.should_use_r128(item) for item in items]): + store_track_gain = self.store_track_r128_gain + store_album_gain = self.store_album_r128_gain + target_level = self.config['r128_targetlevel'].as_number() + peak = Peak.none # R128_* tags do not store the track/album peak + else: + store_track_gain = self.store_track_gain + store_album_gain = self.store_album_gain + target_level = self.config['targetlevel'].as_number() + peak = self._peak_method - self._log.debug(u'applied album gain {0}', album.r128_album_gain) + return store_track_gain, store_album_gain, target_level, peak def handle_album(self, album, write, force=False): """Compute album and track replay gain store it in all of the @@ -928,47 +1111,65 @@ class ReplayGainPlugin(BeetsPlugin): items, nothing is done. """ if not force and not self.album_requires_gain(album): - self._log.info(u'Skipping album {0}', album) + self._log.info('Skipping album {0}', album) return - self._log.info(u'analyzing {0}', album) - if (any([self.should_use_r128(item) for item in album.items()]) and not - all(([self.should_use_r128(item) for item in album.items()]))): - raise ReplayGainError( - u"Mix of ReplayGain and EBU R128 detected" - u" for some tracks in album {0}".format(album) - ) + all([self.should_use_r128(item) for item in album.items()])): + self._log.error( + "Cannot calculate gain for album {0} (incompatible formats)", + album) + return - if any([self.should_use_r128(item) for item in album.items()]): - if self.r128_backend_instance == '': - self.init_r128_backend() - backend_instance = self.r128_backend_instance - store_track_gain = self.store_track_r128_gain - store_album_gain = self.store_album_r128_gain + self._log.info('analyzing {0}', album) + + tag_vals = self.tag_specific_values(album.items()) + store_track_gain, store_album_gain, target_level, peak = tag_vals + + discs = {} + if self.per_disc: + for item in album.items(): + if discs.get(item.disc) is None: + discs[item.disc] = [] + discs[item.disc].append(item) else: - backend_instance = self.backend_instance - store_track_gain = self.store_track_gain - store_album_gain = self.store_album_gain + discs[1] = album.items() - try: - album_gain = backend_instance.compute_album_gain(album) - if len(album_gain.track_gains) != len(album.items()): - raise ReplayGainError( - u"ReplayGain backend failed " - u"for some tracks in album {0}".format(album) + for discnumber, items in discs.items(): + def _store_album(album_gain): + if not album_gain or not album_gain.album_gain \ + or len(album_gain.track_gains) != len(items): + # In some cases, backends fail to produce a valid + # `album_gain` without throwing FatalReplayGainError + # => raise non-fatal exception & continue + raise ReplayGainError( + "ReplayGain backend `{}` failed " + "for some tracks in album {}" + .format(self.backend_name, album) + ) + for item, track_gain in zip(items, + album_gain.track_gains): + store_track_gain(item, track_gain) + store_album_gain(item, album_gain.album_gain) + if write: + item.try_write() + self._log.debug('done analyzing {0}', item) + + try: + self._apply( + self.backend_instance.compute_album_gain, args=(), + kwds={ + "items": list(items), + "target_level": target_level, + "peak": peak + }, + callback=_store_album ) - - store_album_gain(album, album_gain.album_gain) - for item, track_gain in zip(album.items(), album_gain.track_gains): - store_track_gain(item, track_gain) - if write: - item.try_write() - except ReplayGainError as e: - self._log.info(u"ReplayGain error: {0}", e) - except FatalReplayGainError as e: - raise ui.UserError( - u"Fatal replay gain error: {0}".format(e)) + except ReplayGainError as e: + self._log.info("ReplayGain error: {0}", e) + except FatalReplayGainError as e: + raise ui.UserError( + f"Fatal replay gain error: {e}") def handle_track(self, item, write, force=False): """Compute track replay gain and store it in the item. @@ -978,83 +1179,190 @@ class ReplayGainPlugin(BeetsPlugin): in the item, nothing is done. """ if not force and not self.track_requires_gain(item): - self._log.info(u'Skipping track {0}', item) + self._log.info('Skipping track {0}', item) return - self._log.info(u'analyzing {0}', item) + tag_vals = self.tag_specific_values([item]) + store_track_gain, store_album_gain, target_level, peak = tag_vals - if self.should_use_r128(item): - if self.r128_backend_instance == '': - self.init_r128_backend() - backend_instance = self.r128_backend_instance - store_track_gain = self.store_track_r128_gain - else: - backend_instance = self.backend_instance - store_track_gain = self.store_track_gain - - try: - track_gains = backend_instance.compute_track_gain([item]) - if len(track_gains) != 1: + def _store_track(track_gains): + if not track_gains or len(track_gains) != 1: + # In some cases, backends fail to produce a valid + # `track_gains` without throwing FatalReplayGainError + # => raise non-fatal exception & continue raise ReplayGainError( - u"ReplayGain backend failed for track {0}".format(item) + "ReplayGain backend `{}` failed for track {}" + .format(self.backend_name, item) ) store_track_gain(item, track_gains[0]) if write: item.try_write() - except ReplayGainError as e: - self._log.info(u"ReplayGain error: {0}", e) - except FatalReplayGainError as e: - raise ui.UserError( - u"Fatal replay gain error: {0}".format(e)) - - def init_r128_backend(self): - backend_name = 'bs1770gain' + self._log.debug('done analyzing {0}', item) try: - self.r128_backend_instance = self.backends[backend_name]( - self.config, self._log + self._apply( + self.backend_instance.compute_track_gain, args=(), + kwds={ + "items": [item], + "target_level": target_level, + "peak": peak, + }, + callback=_store_track ) - except (ReplayGainError, FatalReplayGainError) as e: - raise ui.UserError( - u'replaygain initialization failed: {0}'.format(e)) + except ReplayGainError as e: + self._log.info("ReplayGain error: {0}", e) + except FatalReplayGainError as e: + raise ui.UserError(f"Fatal replay gain error: {e}") - self.r128_backend_instance.method = '--ebu' + def _has_pool(self): + """Check whether a `ThreadPool` is running instance in `self.pool` + """ + if hasattr(self, 'pool'): + if isinstance(self.pool, ThreadPool) and self.pool._state == RUN: + return True + return False + + def open_pool(self, threads): + """Open a `ThreadPool` instance in `self.pool` + """ + if not self._has_pool() and self.backend_instance.do_parallel: + self.pool = ThreadPool(threads) + self.exc_queue = queue.Queue() + + signal.signal(signal.SIGINT, self._interrupt) + + self.exc_watcher = ExceptionWatcher( + self.exc_queue, # threads push exceptions here + self.terminate_pool # abort once an exception occurs + ) + self.exc_watcher.start() + + def _apply(self, func, args, kwds, callback): + if self._has_pool(): + def catch_exc(func, exc_queue, log): + """Wrapper to catch raised exceptions in threads + """ + def wfunc(*args, **kwargs): + try: + return func(*args, **kwargs) + except ReplayGainError as e: + log.info(e.args[0]) # log non-fatal exceptions + except Exception: + exc_queue.put(sys.exc_info()) + return wfunc + + # Wrap function and callback to catch exceptions + func = catch_exc(func, self.exc_queue, self._log) + callback = catch_exc(callback, self.exc_queue, self._log) + + self.pool.apply_async(func, args, kwds, callback) + else: + callback(func(*args, **kwds)) + + def terminate_pool(self): + """Terminate the `ThreadPool` instance in `self.pool` + (e.g. stop execution in case of exception) + """ + # Don't call self._as_pool() here, + # self.pool._state may not be == RUN + if hasattr(self, 'pool') and isinstance(self.pool, ThreadPool): + self.pool.terminate() + self.pool.join() + # self.exc_watcher.join() + + def _interrupt(self, signal, frame): + try: + self._log.info('interrupted') + self.terminate_pool() + sys.exit(0) + except SystemExit: + # Silence raised SystemExit ~ exit(0) + pass + + def close_pool(self): + """Close the `ThreadPool` instance in `self.pool` (if there is one) + """ + if self._has_pool(): + self.pool.close() + self.pool.join() + self.exc_watcher.join() + + def import_begin(self, session): + """Handle `import_begin` event -> open pool + """ + threads = self.config['threads'].get(int) + + if self.config['parallel_on_import'] \ + and self.config['auto'] \ + and threads: + self.open_pool(threads) + + def import_end(self, paths): + """Handle `import` event -> close pool + """ + self.close_pool() def imported(self, session, task): """Add replay gain info to items or albums of ``task``. """ - if task.is_album: - self.handle_album(task.album, False) - else: - self.handle_track(task.item, False) + if self.config['auto']: + if task.is_album: + self.handle_album(task.album, False) + else: + self.handle_track(task.item, False) + + def command_func(self, lib, opts, args): + try: + write = ui.should_write(opts.write) + force = opts.force + + # Bypass self.open_pool() if called with `--threads 0` + if opts.threads != 0: + threads = opts.threads or self.config['threads'].get(int) + self.open_pool(threads) + + if opts.album: + albums = lib.albums(ui.decargs(args)) + self._log.info( + "Analyzing {} albums ~ {} backend..." + .format(len(albums), self.backend_name) + ) + for album in albums: + self.handle_album(album, write, force) + else: + items = lib.items(ui.decargs(args)) + self._log.info( + "Analyzing {} tracks ~ {} backend..." + .format(len(items), self.backend_name) + ) + for item in items: + self.handle_track(item, write, force) + + self.close_pool() + except (SystemExit, KeyboardInterrupt): + # Silence interrupt exceptions + pass def commands(self): """Return the "replaygain" ui subcommand. """ - def func(lib, opts, args): - write = ui.should_write(opts.write) - force = opts.force - - if opts.album: - for album in lib.albums(ui.decargs(args)): - self.handle_album(album, write, force) - - else: - for item in lib.items(ui.decargs(args)): - self.handle_track(item, write, force) - - cmd = ui.Subcommand('replaygain', help=u'analyze for ReplayGain') + cmd = ui.Subcommand('replaygain', help='analyze for ReplayGain') cmd.parser.add_album_option() + cmd.parser.add_option( + "-t", "--threads", dest="threads", type=int, + help='change the number of threads, \ + defaults to maximum available processors' + ) cmd.parser.add_option( "-f", "--force", dest="force", action="store_true", default=False, - help=u"analyze all files, including those that " + help="analyze all files, including those that " "already have ReplayGain metadata") cmd.parser.add_option( "-w", "--write", default=None, action="store_true", - help=u"write new metadata to files' tags") + help="write new metadata to files' tags") cmd.parser.add_option( "-W", "--nowrite", dest="write", action="store_false", - help=u"don't write metadata (opposite of -w)") - cmd.func = func + help="don't write metadata (opposite of -w)") + cmd.func = self.command_func return [cmd] diff --git a/libs/common/beetsplug/rewrite.py b/libs/common/beetsplug/rewrite.py index eadb1425..e02e4080 100644 --- a/libs/common/beetsplug/rewrite.py +++ b/libs/common/beetsplug/rewrite.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -16,7 +15,6 @@ """Uses user-specified rewriting rules to canonicalize names for path formats. """ -from __future__ import division, absolute_import, print_function import re from collections import defaultdict @@ -44,7 +42,7 @@ def rewriter(field, rules): class RewritePlugin(BeetsPlugin): def __init__(self): - super(RewritePlugin, self).__init__() + super().__init__() self.config.add({}) @@ -55,11 +53,11 @@ class RewritePlugin(BeetsPlugin): try: fieldname, pattern = key.split(None, 1) except ValueError: - raise ui.UserError(u"invalid rewrite specification") + raise ui.UserError("invalid rewrite specification") if fieldname not in library.Item._fields: - raise ui.UserError(u"invalid field name (%s) in rewriter" % + raise ui.UserError("invalid field name (%s) in rewriter" % fieldname) - self._log.debug(u'adding template field {0}', key) + self._log.debug('adding template field {0}', key) pattern = re.compile(pattern.lower()) rules[fieldname].append((pattern, value)) if fieldname == 'artist': diff --git a/libs/common/beetsplug/scrub.py b/libs/common/beetsplug/scrub.py index be6e7fd1..d8044668 100644 --- a/libs/common/beetsplug/scrub.py +++ b/libs/common/beetsplug/scrub.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -17,13 +16,12 @@ automatically whenever tags are written. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui from beets import util from beets import config -from beets import mediafile +import mediafile import mutagen _MUTAGEN_FORMATS = { @@ -48,7 +46,7 @@ _MUTAGEN_FORMATS = { class ScrubPlugin(BeetsPlugin): """Removes extraneous metadata from files' tags.""" def __init__(self): - super(ScrubPlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, }) @@ -60,15 +58,15 @@ class ScrubPlugin(BeetsPlugin): def scrub_func(lib, opts, args): # Walk through matching files and remove tags. for item in lib.items(ui.decargs(args)): - self._log.info(u'scrubbing: {0}', + self._log.info('scrubbing: {0}', util.displayable_path(item.path)) self._scrub_item(item, opts.write) - scrub_cmd = ui.Subcommand('scrub', help=u'clean audio tags') + scrub_cmd = ui.Subcommand('scrub', help='clean audio tags') scrub_cmd.parser.add_option( - u'-W', u'--nowrite', dest='write', + '-W', '--nowrite', dest='write', action='store_false', default=True, - help=u'leave tags empty') + help='leave tags empty') scrub_cmd.func = scrub_func return [scrub_cmd] @@ -79,7 +77,7 @@ class ScrubPlugin(BeetsPlugin): """ classes = [] for modname, clsname in _MUTAGEN_FORMATS.items(): - mod = __import__('mutagen.{0}'.format(modname), + mod = __import__(f'mutagen.{modname}', fromlist=[clsname]) classes.append(getattr(mod, clsname)) return classes @@ -107,8 +105,8 @@ class ScrubPlugin(BeetsPlugin): for tag in f.keys(): del f[tag] f.save() - except (IOError, mutagen.MutagenError) as exc: - self._log.error(u'could not scrub {0}: {1}', + except (OSError, mutagen.MutagenError) as exc: + self._log.error('could not scrub {0}: {1}', util.displayable_path(path), exc) def _scrub_item(self, item, restore=True): @@ -121,7 +119,7 @@ class ScrubPlugin(BeetsPlugin): mf = mediafile.MediaFile(util.syspath(item.path), config['id3v23'].get(bool)) except mediafile.UnreadableFileError as exc: - self._log.error(u'could not open file to scrub: {0}', + self._log.error('could not open file to scrub: {0}', exc) return images = mf.images @@ -131,21 +129,21 @@ class ScrubPlugin(BeetsPlugin): # Restore tags, if enabled. if restore: - self._log.debug(u'writing new tags after scrub') + self._log.debug('writing new tags after scrub') item.try_write() if images: - self._log.debug(u'restoring art') + self._log.debug('restoring art') try: mf = mediafile.MediaFile(util.syspath(item.path), config['id3v23'].get(bool)) mf.images = images mf.save() except mediafile.UnreadableFileError as exc: - self._log.error(u'could not write tags: {0}', exc) + self._log.error('could not write tags: {0}', exc) def import_task_files(self, session, task): """Automatically scrub imported files.""" for item in task.imported_items(): - self._log.debug(u'auto-scrubbing {0}', + self._log.debug('auto-scrubbing {0}', util.displayable_path(item.path)) self._scrub_item(item) diff --git a/libs/common/beetsplug/smartplaylist.py b/libs/common/beetsplug/smartplaylist.py index 009512c5..4c921ecc 100644 --- a/libs/common/beetsplug/smartplaylist.py +++ b/libs/common/beetsplug/smartplaylist.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Dang Mai . # @@ -16,30 +15,38 @@ """Generates smart playlists based on beets queries. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui from beets.util import (mkdirall, normpath, sanitize_path, syspath, - bytestring_path) + bytestring_path, path_as_posix) from beets.library import Item, Album, parse_query_string from beets.dbcore import OrQuery from beets.dbcore.query import MultipleSort, ParsingError import os -import six + +try: + from urllib.request import pathname2url +except ImportError: + # python2 is a bit different + from urllib import pathname2url class SmartPlaylistPlugin(BeetsPlugin): def __init__(self): - super(SmartPlaylistPlugin, self).__init__() + super().__init__() self.config.add({ 'relative_to': None, - 'playlist_dir': u'.', + 'playlist_dir': '.', 'auto': True, - 'playlists': [] + 'playlists': [], + 'forward_slash': False, + 'prefix': '', + 'urlencode': False, }) + self.config['prefix'].redact = True # May contain username/password. self._matched_playlists = None self._unmatched_playlists = None @@ -49,8 +56,8 @@ class SmartPlaylistPlugin(BeetsPlugin): def commands(self): spl_update = ui.Subcommand( 'splupdate', - help=u'update the smart playlists. Playlist names may be ' - u'passed as arguments.' + help='update the smart playlists. Playlist names may be ' + 'passed as arguments.' ) spl_update.func = self.update_cmd return [spl_update] @@ -61,14 +68,14 @@ class SmartPlaylistPlugin(BeetsPlugin): args = set(ui.decargs(args)) for a in list(args): if not a.endswith(".m3u"): - args.add("{0}.m3u".format(a)) + args.add(f"{a}.m3u") - playlists = set((name, q, a_q) - for name, q, a_q in self._unmatched_playlists - if name in args) + playlists = {(name, q, a_q) + for name, q, a_q in self._unmatched_playlists + if name in args} if not playlists: raise ui.UserError( - u'No playlist matching any of {0} found'.format( + 'No playlist matching any of {} found'.format( [name for name, _, _ in self._unmatched_playlists]) ) @@ -81,7 +88,7 @@ class SmartPlaylistPlugin(BeetsPlugin): def build_queries(self): """ - Instanciate queries for the playlists. + Instantiate queries for the playlists. Each playlist has 2 queries: one or items one for albums, each with a sort. We must also remember its name. _unmatched_playlists is a set of @@ -99,22 +106,23 @@ class SmartPlaylistPlugin(BeetsPlugin): for playlist in self.config['playlists'].get(list): if 'name' not in playlist: - self._log.warning(u"playlist configuration is missing name") + self._log.warning("playlist configuration is missing name") continue playlist_data = (playlist['name'],) try: - for key, Model in (('query', Item), ('album_query', Album)): + for key, model_cls in (('query', Item), + ('album_query', Album)): qs = playlist.get(key) if qs is None: query_and_sort = None, None - elif isinstance(qs, six.string_types): - query_and_sort = parse_query_string(qs, Model) + elif isinstance(qs, str): + query_and_sort = parse_query_string(qs, model_cls) elif len(qs) == 1: - query_and_sort = parse_query_string(qs[0], Model) + query_and_sort = parse_query_string(qs[0], model_cls) else: # multiple queries and sorts - queries, sorts = zip(*(parse_query_string(q, Model) + queries, sorts = zip(*(parse_query_string(q, model_cls) for q in qs)) query = OrQuery(queries) final_sorts = [] @@ -135,7 +143,7 @@ class SmartPlaylistPlugin(BeetsPlugin): playlist_data += (query_and_sort,) except ParsingError as exc: - self._log.warning(u"invalid query in playlist {}: {}", + self._log.warning("invalid query in playlist {}: {}", playlist['name'], exc) continue @@ -156,14 +164,14 @@ class SmartPlaylistPlugin(BeetsPlugin): n, (q, _), (a_q, _) = playlist if self.matches(model, q, a_q): self._log.debug( - u"{0} will be updated because of {1}", n, model) + "{0} will be updated because of {1}", n, model) self._matched_playlists.add(playlist) self.register_listener('cli_exit', self.update_playlists) self._unmatched_playlists -= self._matched_playlists def update_playlists(self, lib): - self._log.info(u"Updating {0} smart playlists...", + self._log.info("Updating {0} smart playlists...", len(self._matched_playlists)) playlist_dir = self.config['playlist_dir'].as_filename() @@ -177,7 +185,7 @@ class SmartPlaylistPlugin(BeetsPlugin): for playlist in self._matched_playlists: name, (query, q_sort), (album_query, a_q_sort) = playlist - self._log.debug(u"Creating playlist {0}", name) + self._log.debug("Creating playlist {0}", name) items = [] if query: @@ -199,6 +207,7 @@ class SmartPlaylistPlugin(BeetsPlugin): if item_path not in m3us[m3u_name]: m3us[m3u_name].append(item_path) + prefix = bytestring_path(self.config['prefix'].as_str()) # Write all of the accumulated track lists to files. for m3u in m3us: m3u_path = normpath(os.path.join(playlist_dir, @@ -206,6 +215,10 @@ class SmartPlaylistPlugin(BeetsPlugin): mkdirall(m3u_path) with open(syspath(m3u_path), 'wb') as f: for path in m3us[m3u]: - f.write(path + b'\n') + if self.config['forward_slash'].get(): + path = path_as_posix(path) + if self.config['urlencode']: + path = bytestring_path(pathname2url(path)) + f.write(prefix + path + b'\n') - self._log.info(u"{0} playlists updated", len(self._matched_playlists)) + self._log.info("{0} playlists updated", len(self._matched_playlists)) diff --git a/libs/common/beetsplug/sonosupdate.py b/libs/common/beetsplug/sonosupdate.py index 56a315a1..aeb211d8 100644 --- a/libs/common/beetsplug/sonosupdate.py +++ b/libs/common/beetsplug/sonosupdate.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2018, Tobias Sauerwein. # @@ -16,7 +15,6 @@ """Updates a Sonos library whenever the beets library is changed. This is based on the Kodi Update plugin. """ -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin import soco @@ -24,7 +22,7 @@ import soco class SonosUpdate(BeetsPlugin): def __init__(self): - super(SonosUpdate, self).__init__() + super().__init__() self.register_listener('database_change', self.listen_for_db_change) def listen_for_db_change(self, lib, model): @@ -35,14 +33,14 @@ class SonosUpdate(BeetsPlugin): """When the client exists try to send refresh request to a Sonos controler. """ - self._log.info(u'Requesting a Sonos library update...') + self._log.info('Requesting a Sonos library update...') device = soco.discovery.any_soco() if device: device.music_library.start_library_update() else: - self._log.warning(u'Could not find a Sonos device.') + self._log.warning('Could not find a Sonos device.') return - self._log.info(u'Sonos update triggered') + self._log.info('Sonos update triggered') diff --git a/libs/common/beetsplug/spotify.py b/libs/common/beetsplug/spotify.py index 36231f29..2529160d 100644 --- a/libs/common/beetsplug/spotify.py +++ b/libs/common/beetsplug/spotify.py @@ -1,61 +1,379 @@ -# -*- coding: utf-8 -*- +# This file is part of beets. +# Copyright 2019, Rahul Ahuja. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function +"""Adds Spotify release and track search support to the autotagger, along with +Spotify playlist construction. +""" import re +import json +import base64 import webbrowser +import collections + +import unidecode import requests -from beets.plugins import BeetsPlugin -from beets.ui import decargs +import confuse + from beets import ui -from requests.exceptions import HTTPError +from beets.autotag.hooks import AlbumInfo, TrackInfo +from beets.plugins import MetadataSourcePlugin, BeetsPlugin -class SpotifyPlugin(BeetsPlugin): +class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin): + data_source = 'Spotify' - # URL for the Web API of Spotify - # Documentation here: https://developer.spotify.com/web-api/search-item/ - base_url = "https://api.spotify.com/v1/search" - open_url = "http://open.spotify.com/track/" - playlist_partial = "spotify:trackset:Playlist:" + # Base URLs for the Spotify API + # Documentation: https://developer.spotify.com/web-api + oauth_token_url = 'https://accounts.spotify.com/api/token' + open_track_url = 'https://open.spotify.com/track/' + search_url = 'https://api.spotify.com/v1/search' + album_url = 'https://api.spotify.com/v1/albums/' + track_url = 'https://api.spotify.com/v1/tracks/' + + # Spotify IDs consist of 22 alphanumeric characters + # (zero-left-padded base62 representation of randomly generated UUID4) + id_regex = { + 'pattern': r'(^|open\.spotify\.com/{}/)([0-9A-Za-z]{{22}})', + 'match_group': 2, + } def __init__(self): - super(SpotifyPlugin, self).__init__() - self.config.add({ - 'mode': 'list', - 'tiebreak': 'popularity', - 'show_failures': False, - 'artist_field': 'albumartist', - 'album_field': 'album', - 'track_field': 'title', - 'region_filter': None, - 'regex': [] - }) + super().__init__() + self.config.add( + { + 'mode': 'list', + 'tiebreak': 'popularity', + 'show_failures': False, + 'artist_field': 'albumartist', + 'album_field': 'album', + 'track_field': 'title', + 'region_filter': None, + 'regex': [], + 'client_id': '4e414367a1d14c75a5c5129a627fcab8', + 'client_secret': 'f82bdc09b2254f1a8286815d02fd46dc', + 'tokenfile': 'spotify_token.json', + } + ) + self.config['client_secret'].redact = True + + self.tokenfile = self.config['tokenfile'].get( + confuse.Filename(in_app_dir=True) + ) # Path to the JSON file for storing the OAuth access token. + self.setup() + + def setup(self): + """Retrieve previously saved OAuth token or generate a new one.""" + try: + with open(self.tokenfile) as f: + token_data = json.load(f) + except OSError: + self._authenticate() + else: + self.access_token = token_data['access_token'] + + def _authenticate(self): + """Request an access token via the Client Credentials Flow: + https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow + """ + headers = { + 'Authorization': 'Basic {}'.format( + base64.b64encode( + ':'.join( + self.config[k].as_str() + for k in ('client_id', 'client_secret') + ).encode() + ).decode() + ) + } + response = requests.post( + self.oauth_token_url, + data={'grant_type': 'client_credentials'}, + headers=headers, + ) + try: + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise ui.UserError( + 'Spotify authorization failed: {}\n{}'.format( + e, response.text + ) + ) + self.access_token = response.json()['access_token'] + + # Save the token for later use. + self._log.debug( + '{} access token: {}', self.data_source, self.access_token + ) + with open(self.tokenfile, 'w') as f: + json.dump({'access_token': self.access_token}, f) + + def _handle_response(self, request_type, url, params=None): + """Send a request, reauthenticating if necessary. + + :param request_type: Type of :class:`Request` constructor, + e.g. ``requests.get``, ``requests.post``, etc. + :type request_type: function + :param url: URL for the new :class:`Request` object. + :type url: str + :param params: (optional) list of tuples or bytes to send + in the query string for the :class:`Request`. + :type params: dict + :return: JSON data for the class:`Response ` object. + :rtype: dict + """ + response = request_type( + url, + headers={'Authorization': f'Bearer {self.access_token}'}, + params=params, + ) + if response.status_code != 200: + if 'token expired' in response.text: + self._log.debug( + '{} access token has expired. Reauthenticating.', + self.data_source, + ) + self._authenticate() + return self._handle_response(request_type, url, params=params) + else: + raise ui.UserError( + '{} API error:\n{}\nURL:\n{}\nparams:\n{}'.format( + self.data_source, response.text, url, params + ) + ) + return response.json() + + def album_for_id(self, album_id): + """Fetch an album by its Spotify ID or URL and return an + AlbumInfo object or None if the album is not found. + + :param album_id: Spotify ID or URL for the album + :type album_id: str + :return: AlbumInfo object for album + :rtype: beets.autotag.hooks.AlbumInfo or None + """ + spotify_id = self._get_id('album', album_id) + if spotify_id is None: + return None + + album_data = self._handle_response( + requests.get, self.album_url + spotify_id + ) + artist, artist_id = self.get_artist(album_data['artists']) + + date_parts = [ + int(part) for part in album_data['release_date'].split('-') + ] + + release_date_precision = album_data['release_date_precision'] + if release_date_precision == 'day': + year, month, day = date_parts + elif release_date_precision == 'month': + year, month = date_parts + day = None + elif release_date_precision == 'year': + year = date_parts[0] + month = None + day = None + else: + raise ui.UserError( + "Invalid `release_date_precision` returned " + "by {} API: '{}'".format( + self.data_source, release_date_precision + ) + ) + + tracks = [] + medium_totals = collections.defaultdict(int) + for i, track_data in enumerate(album_data['tracks']['items'], start=1): + track = self._get_track(track_data) + track.index = i + medium_totals[track.medium] += 1 + tracks.append(track) + for track in tracks: + track.medium_total = medium_totals[track.medium] + + return AlbumInfo( + album=album_data['name'], + album_id=spotify_id, + artist=artist, + artist_id=artist_id, + tracks=tracks, + albumtype=album_data['album_type'], + va=len(album_data['artists']) == 1 + and artist.lower() == 'various artists', + year=year, + month=month, + day=day, + label=album_data['label'], + mediums=max(medium_totals.keys()), + data_source=self.data_source, + data_url=album_data['external_urls']['spotify'], + ) + + def _get_track(self, track_data): + """Convert a Spotify track object dict to a TrackInfo object. + + :param track_data: Simplified track object + (https://developer.spotify.com/documentation/web-api/reference/object-model/#track-object-simplified) + :type track_data: dict + :return: TrackInfo object for track + :rtype: beets.autotag.hooks.TrackInfo + """ + artist, artist_id = self.get_artist(track_data['artists']) + return TrackInfo( + title=track_data['name'], + track_id=track_data['id'], + artist=artist, + artist_id=artist_id, + length=track_data['duration_ms'] / 1000, + index=track_data['track_number'], + medium=track_data['disc_number'], + medium_index=track_data['track_number'], + data_source=self.data_source, + data_url=track_data['external_urls']['spotify'], + ) + + def track_for_id(self, track_id=None, track_data=None): + """Fetch a track by its Spotify ID or URL and return a + TrackInfo object or None if the track is not found. + + :param track_id: (Optional) Spotify ID or URL for the track. Either + ``track_id`` or ``track_data`` must be provided. + :type track_id: str + :param track_data: (Optional) Simplified track object dict. May be + provided instead of ``track_id`` to avoid unnecessary API calls. + :type track_data: dict + :return: TrackInfo object for track + :rtype: beets.autotag.hooks.TrackInfo or None + """ + if track_data is None: + spotify_id = self._get_id('track', track_id) + if spotify_id is None: + return None + track_data = self._handle_response( + requests.get, self.track_url + spotify_id + ) + track = self._get_track(track_data) + + # Get album's tracks to set `track.index` (position on the entire + # release) and `track.medium_total` (total number of tracks on + # the track's disc). + album_data = self._handle_response( + requests.get, self.album_url + track_data['album']['id'] + ) + medium_total = 0 + for i, track_data in enumerate(album_data['tracks']['items'], start=1): + if track_data['disc_number'] == track.medium: + medium_total += 1 + if track_data['id'] == track.track_id: + track.index = i + track.medium_total = medium_total + return track + + @staticmethod + def _construct_search_query(filters=None, keywords=''): + """Construct a query string with the specified filters and keywords to + be provided to the Spotify Search API + (https://developer.spotify.com/documentation/web-api/reference/search/search/#writing-a-query---guidelines). + + :param filters: (Optional) Field filters to apply. + :type filters: dict + :param keywords: (Optional) Query keywords to use. + :type keywords: str + :return: Query string to be provided to the Search API. + :rtype: str + """ + query_components = [ + keywords, + ' '.join(':'.join((k, v)) for k, v in filters.items()), + ] + query = ' '.join([q for q in query_components if q]) + if not isinstance(query, str): + query = query.decode('utf8') + return unidecode.unidecode(query) + + def _search_api(self, query_type, filters=None, keywords=''): + """Query the Spotify Search API for the specified ``keywords``, applying + the provided ``filters``. + + :param query_type: Item type to search across. Valid types are: + 'album', 'artist', 'playlist', and 'track'. + :type query_type: str + :param filters: (Optional) Field filters to apply. + :type filters: dict + :param keywords: (Optional) Query keywords to use. + :type keywords: str + :return: JSON data for the class:`Response ` object or None + if no search results are returned. + :rtype: dict or None + """ + query = self._construct_search_query( + keywords=keywords, filters=filters + ) + if not query: + return None + self._log.debug( + f"Searching {self.data_source} for '{query}'" + ) + response_data = ( + self._handle_response( + requests.get, + self.search_url, + params={'q': query, 'type': query_type}, + ) + .get(query_type + 's', {}) + .get('items', []) + ) + self._log.debug( + "Found {} result(s) from {} for '{}'", + len(response_data), + self.data_source, + query, + ) + return response_data def commands(self): def queries(lib, opts, args): - success = self.parse_opts(opts) + success = self._parse_opts(opts) if success: - results = self.query_spotify(lib, decargs(args)) - self.output_results(results) + results = self._match_library_tracks(lib, ui.decargs(args)) + self._output_match_results(results) + spotify_cmd = ui.Subcommand( - 'spotify', - help=u'build a Spotify playlist' + 'spotify', help=f'build a {self.data_source} playlist' ) spotify_cmd.parser.add_option( - u'-m', u'--mode', action='store', - help=u'"open" to open Spotify with playlist, ' - u'"list" to print (default)' + '-m', + '--mode', + action='store', + help='"open" to open {} with playlist, ' + '"list" to print (default)'.format(self.data_source), ) spotify_cmd.parser.add_option( - u'-f', u'--show-failures', - action='store_true', dest='show_failures', - help=u'list tracks that did not match a Spotify ID' + '-f', + '--show-failures', + action='store_true', + dest='show_failures', + help='list tracks that did not match a {} ID'.format( + self.data_source + ), ) spotify_cmd.func = queries return [spotify_cmd] - def parse_opts(self, opts): + def _parse_opts(self, opts): if opts.mode: self.config['mode'].set(opts.mode) @@ -63,35 +381,47 @@ class SpotifyPlugin(BeetsPlugin): self.config['show_failures'].set(True) if self.config['mode'].get() not in ['list', 'open']: - self._log.warning(u'{0} is not a valid mode', - self.config['mode'].get()) + self._log.warning( + '{0} is not a valid mode', self.config['mode'].get() + ) return False self.opts = opts return True - def query_spotify(self, lib, query): + def _match_library_tracks(self, library, keywords): + """Get a list of simplified track object dicts for library tracks + matching the specified ``keywords``. + :param library: beets library object to query. + :type library: beets.library.Library + :param keywords: Query to match library items against. + :type keywords: str + :return: List of simplified track object dicts for library items + matching the specified query. + :rtype: list[dict] + """ results = [] failures = [] - items = lib.items(query) + items = library.items(keywords) if not items: - self._log.debug(u'Your beets query returned no items, ' - u'skipping spotify') + self._log.debug( + 'Your beets query returned no items, skipping {}.', + self.data_source, + ) return - self._log.info(u'Processing {0} tracks...', len(items)) + self._log.info('Processing {} tracks...', len(items)) for item in items: - # Apply regex transformations if provided for regex in self.config['regex'].get(): if ( - not regex['field'] or - not regex['search'] or - not regex['replace'] + not regex['field'] + or not regex['search'] + or not regex['replace'] ): continue @@ -103,73 +433,95 @@ class SpotifyPlugin(BeetsPlugin): # Custom values can be passed in the config (just in case) artist = item[self.config['artist_field'].get()] album = item[self.config['album_field'].get()] - query = item[self.config['track_field'].get()] - search_url = query + " album:" + album + " artist:" + artist + keywords = item[self.config['track_field'].get()] # Query the Web API for each track, look for the items' JSON data - r = requests.get(self.base_url, params={ - "q": search_url, "type": "track" - }) - self._log.debug('{}', r.url) - try: - r.raise_for_status() - except HTTPError as e: - self._log.debug(u'URL returned a {0} error', - e.response.status_code) - failures.append(search_url) + query_filters = {'artist': artist, 'album': album} + response_data_tracks = self._search_api( + query_type='track', keywords=keywords, filters=query_filters + ) + if not response_data_tracks: + query = self._construct_search_query( + keywords=keywords, filters=query_filters + ) + failures.append(query) continue - r_data = r.json()['tracks']['items'] - # Apply market filter if requested region_filter = self.config['region_filter'].get() if region_filter: - r_data = [x for x in r_data if region_filter - in x['available_markets']] + response_data_tracks = [ + track_data + for track_data in response_data_tracks + if region_filter in track_data['available_markets'] + ] - # Simplest, take the first result - chosen_result = None - if len(r_data) == 1 or self.config['tiebreak'].get() == "first": - self._log.debug(u'Spotify track(s) found, count: {0}', - len(r_data)) - chosen_result = r_data[0] - elif len(r_data) > 1: - # Use the popularity filter - self._log.debug(u'Most popular track chosen, count: {0}', - len(r_data)) - chosen_result = max(r_data, key=lambda x: x['popularity']) - - if chosen_result: - results.append(chosen_result) + if ( + len(response_data_tracks) == 1 + or self.config['tiebreak'].get() == 'first' + ): + self._log.debug( + '{} track(s) found, count: {}', + self.data_source, + len(response_data_tracks), + ) + chosen_result = response_data_tracks[0] else: - self._log.debug(u'No spotify track found: {0}', search_url) - failures.append(search_url) + # Use the popularity filter + self._log.debug( + 'Most popular track chosen, count: {}', + len(response_data_tracks), + ) + chosen_result = max( + response_data_tracks, key=lambda x: x['popularity'] + ) + results.append(chosen_result) failure_count = len(failures) if failure_count > 0: if self.config['show_failures'].get(): - self._log.info(u'{0} track(s) did not match a Spotify ID:', - failure_count) + self._log.info( + '{} track(s) did not match a {} ID:', + failure_count, + self.data_source, + ) for track in failures: - self._log.info(u'track: {0}', track) - self._log.info(u'') + self._log.info('track: {}', track) + self._log.info('') else: - self._log.warning(u'{0} track(s) did not match a Spotify ID;\n' - u'use --show-failures to display', - failure_count) + self._log.warning( + '{} track(s) did not match a {} ID:\n' + 'use --show-failures to display', + failure_count, + self.data_source, + ) return results - def output_results(self, results): - if results: - ids = [x['id'] for x in results] - if self.config['mode'].get() == "open": - self._log.info(u'Attempting to open Spotify with playlist') - spotify_url = self.playlist_partial + ",".join(ids) - webbrowser.open(spotify_url) + def _output_match_results(self, results): + """Open a playlist or print Spotify URLs for the provided track + object dicts. + :param results: List of simplified track object dicts + (https://developer.spotify.com/documentation/web-api/reference/object-model/#track-object-simplified) + :type results: list[dict] + """ + if results: + spotify_ids = [track_data['id'] for track_data in results] + if self.config['mode'].get() == 'open': + self._log.info( + 'Attempting to open {} with playlist'.format( + self.data_source + ) + ) + spotify_url = 'spotify:trackset:Playlist:' + ','.join( + spotify_ids + ) + webbrowser.open(spotify_url) else: - for item in ids: - print(self.open_url + item) + for spotify_id in spotify_ids: + print(self.open_track_url + spotify_id) else: - self._log.warning(u'No Spotify tracks found from beets query') + self._log.warning( + f'No {self.data_source} tracks found from beets query' + ) diff --git a/libs/common/beetsplug/subsonicplaylist.py b/libs/common/beetsplug/subsonicplaylist.py new file mode 100644 index 00000000..ead78919 --- /dev/null +++ b/libs/common/beetsplug/subsonicplaylist.py @@ -0,0 +1,171 @@ +# This file is part of beets. +# Copyright 2019, Joris Jensen +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + + +import random +import string +from xml.etree import ElementTree +from hashlib import md5 +from urllib.parse import urlencode + +import requests + +from beets.dbcore import AndQuery +from beets.dbcore.query import MatchQuery +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand + +__author__ = 'https://github.com/MrNuggelz' + + +def filter_to_be_removed(items, keys): + if len(items) > len(keys): + dont_remove = [] + for artist, album, title in keys: + for item in items: + if artist == item['artist'] and \ + album == item['album'] and \ + title == item['title']: + dont_remove.append(item) + return [item for item in items if item not in dont_remove] + else: + def to_be_removed(item): + for artist, album, title in keys: + if artist == item['artist'] and\ + album == item['album'] and\ + title == item['title']: + return False + return True + + return [item for item in items if to_be_removed(item)] + + +class SubsonicPlaylistPlugin(BeetsPlugin): + + def __init__(self): + super().__init__() + self.config.add( + { + 'delete': False, + 'playlist_ids': [], + 'playlist_names': [], + 'username': '', + 'password': '' + } + ) + self.config['password'].redact = True + + def update_tags(self, playlist_dict, lib): + with lib.transaction(): + for query, playlist_tag in playlist_dict.items(): + query = AndQuery([MatchQuery("artist", query[0]), + MatchQuery("album", query[1]), + MatchQuery("title", query[2])]) + items = lib.items(query) + if not items: + self._log.warn("{} | track not found ({})", playlist_tag, + query) + continue + for item in items: + item.subsonic_playlist = playlist_tag + item.try_sync(write=True, move=False) + + def get_playlist(self, playlist_id): + xml = self.send('getPlaylist', {'id': playlist_id}).text + playlist = ElementTree.fromstring(xml)[0] + if playlist.attrib.get('code', '200') != '200': + alt_error = 'error getting playlist, but no error message found' + self._log.warn(playlist.attrib.get('message', alt_error)) + return + + name = playlist.attrib.get('name', 'undefined') + tracks = [(t.attrib['artist'], t.attrib['album'], t.attrib['title']) + for t in playlist] + return name, tracks + + def commands(self): + def build_playlist(lib, opts, args): + self.config.set_args(opts) + ids = self.config['playlist_ids'].as_str_seq() + if self.config['playlist_names'].as_str_seq(): + playlists = ElementTree.fromstring( + self.send('getPlaylists').text)[0] + if playlists.attrib.get('code', '200') != '200': + alt_error = 'error getting playlists,' \ + ' but no error message found' + self._log.warn( + playlists.attrib.get('message', alt_error)) + return + for name in self.config['playlist_names'].as_str_seq(): + for playlist in playlists: + if name == playlist.attrib['name']: + ids.append(playlist.attrib['id']) + + playlist_dict = self.get_playlists(ids) + + # delete old tags + if self.config['delete']: + existing = list(lib.items('subsonic_playlist:";"')) + to_be_removed = filter_to_be_removed( + existing, + playlist_dict.keys()) + for item in to_be_removed: + item['subsonic_playlist'] = '' + with lib.transaction(): + item.try_sync(write=True, move=False) + + self.update_tags(playlist_dict, lib) + + subsonicplaylist_cmds = Subcommand( + 'subsonicplaylist', help='import a subsonic playlist' + ) + subsonicplaylist_cmds.parser.add_option( + '-d', + '--delete', + action='store_true', + help='delete tag from items not in any playlist anymore', + ) + subsonicplaylist_cmds.func = build_playlist + return [subsonicplaylist_cmds] + + def generate_token(self): + salt = ''.join(random.choices(string.ascii_lowercase + string.digits)) + return md5( + (self.config['password'].get() + salt).encode()).hexdigest(), salt + + def send(self, endpoint, params=None): + if params is None: + params = {} + a, b = self.generate_token() + params['u'] = self.config['username'] + params['t'] = a + params['s'] = b + params['v'] = '1.12.0' + params['c'] = 'beets' + resp = requests.get('{}/rest/{}?{}'.format( + self.config['base_url'].get(), + endpoint, + urlencode(params)) + ) + return resp + + def get_playlists(self, ids): + output = {} + for playlist_id in ids: + name, tracks = self.get_playlist(playlist_id) + for track in tracks: + if track not in output: + output[track] = ';' + output[track] += name + ';' + return output diff --git a/libs/common/beetsplug/subsonicupdate.py b/libs/common/beetsplug/subsonicupdate.py new file mode 100644 index 00000000..9480bcb4 --- /dev/null +++ b/libs/common/beetsplug/subsonicupdate.py @@ -0,0 +1,144 @@ +# This file is part of beets. +# Copyright 2016, Adrian Sampson. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Updates Subsonic library on Beets import +Your Beets configuration file should contain +a "subsonic" section like the following: + subsonic: + url: https://mydomain.com:443/subsonic + user: username + pass: password + auth: token +For older Subsonic versions, token authentication +is not supported, use password instead: + subsonic: + url: https://mydomain.com:443/subsonic + user: username + pass: password + auth: pass +""" + +import hashlib +import random +import string + +import requests + +from binascii import hexlify +from beets import config +from beets.plugins import BeetsPlugin + +__author__ = 'https://github.com/maffo999' + + +class SubsonicUpdate(BeetsPlugin): + def __init__(self): + super().__init__() + # Set default configuration values + config['subsonic'].add({ + 'user': 'admin', + 'pass': 'admin', + 'url': 'http://localhost:4040', + 'auth': 'token', + }) + config['subsonic']['pass'].redact = True + self.register_listener('import', self.start_scan) + + @staticmethod + def __create_token(): + """Create salt and token from given password. + + :return: The generated salt and hashed token + """ + password = config['subsonic']['pass'].as_str() + + # Pick the random sequence and salt the password + r = string.ascii_letters + string.digits + salt = "".join([random.choice(r) for _ in range(6)]) + salted_password = password + salt + token = hashlib.md5(salted_password.encode('utf-8')).hexdigest() + + # Put together the payload of the request to the server and the URL + return salt, token + + @staticmethod + def __format_url(endpoint): + """Get the Subsonic URL to trigger the given endpoint. + Uses either the url config option or the deprecated host, port, + and context_path config options together. + + :return: Endpoint for updating Subsonic + """ + + url = config['subsonic']['url'].as_str() + if url and url.endswith('/'): + url = url[:-1] + + # @deprecated("Use url config option instead") + if not url: + host = config['subsonic']['host'].as_str() + port = config['subsonic']['port'].get(int) + context_path = config['subsonic']['contextpath'].as_str() + if context_path == '/': + context_path = '' + url = f"http://{host}:{port}{context_path}" + + return url + f'/rest/{endpoint}' + + def start_scan(self): + user = config['subsonic']['user'].as_str() + auth = config['subsonic']['auth'].as_str() + url = self.__format_url("startScan") + self._log.debug('URL is {0}', url) + self._log.debug('auth type is {0}', config['subsonic']['auth']) + + if auth == "token": + salt, token = self.__create_token() + payload = { + 'u': user, + 't': token, + 's': salt, + 'v': '1.13.0', # Subsonic 5.3 and newer + 'c': 'beets', + 'f': 'json' + } + elif auth == "password": + password = config['subsonic']['pass'].as_str() + encpass = hexlify(password.encode()).decode() + payload = { + 'u': user, + 'p': f'enc:{encpass}', + 'v': '1.12.0', + 'c': 'beets', + 'f': 'json' + } + else: + return + try: + response = requests.get(url, params=payload) + json = response.json() + + if response.status_code == 200 and \ + json['subsonic-response']['status'] == "ok": + count = json['subsonic-response']['scanStatus']['count'] + self._log.info( + f'Updating Subsonic; scanning {count} tracks') + elif response.status_code == 200 and \ + json['subsonic-response']['status'] == "failed": + error_message = json['subsonic-response']['error']['message'] + self._log.error(f'Error: {error_message}') + else: + self._log.error('Error: {0}', json) + except Exception as error: + self._log.error(f'Error: {error}') diff --git a/libs/common/beetsplug/the.py b/libs/common/beetsplug/the.py index cfb583ce..e6626d2b 100644 --- a/libs/common/beetsplug/the.py +++ b/libs/common/beetsplug/the.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Blemjhoo Tezoulbr . # @@ -15,7 +14,6 @@ """Moves patterns in path formats (suitable for moving articles).""" -from __future__ import division, absolute_import, print_function import re from beets.plugins import BeetsPlugin @@ -23,9 +21,9 @@ from beets.plugins import BeetsPlugin __author__ = 'baobab@heresiarch.info' __version__ = '1.1' -PATTERN_THE = u'^[the]{3}\s' -PATTERN_A = u'^[a][n]?\s' -FORMAT = u'{0}, {1}' +PATTERN_THE = '^the\\s' +PATTERN_A = '^[a][n]?\\s' +FORMAT = '{0}, {1}' class ThePlugin(BeetsPlugin): @@ -33,14 +31,14 @@ class ThePlugin(BeetsPlugin): patterns = [] def __init__(self): - super(ThePlugin, self).__init__() + super().__init__() self.template_funcs['the'] = self.the_template_func self.config.add({ 'the': True, 'a': True, - 'format': u'{0}, {1}', + 'format': '{0}, {1}', 'strip': False, 'patterns': [], }) @@ -51,17 +49,17 @@ class ThePlugin(BeetsPlugin): try: re.compile(p) except re.error: - self._log.error(u'invalid pattern: {0}', p) + self._log.error('invalid pattern: {0}', p) else: if not (p.startswith('^') or p.endswith('$')): - self._log.warning(u'warning: \"{0}\" will not ' - u'match string start/end', p) + self._log.warning('warning: \"{0}\" will not ' + 'match string start/end', p) if self.config['a']: self.patterns = [PATTERN_A] + self.patterns if self.config['the']: self.patterns = [PATTERN_THE] + self.patterns if not self.patterns: - self._log.warning(u'no patterns defined!') + self._log.warning('no patterns defined!') def unthe(self, text, pattern): """Moves pattern in the path format string or strips it @@ -84,7 +82,7 @@ class ThePlugin(BeetsPlugin): fmt = self.config['format'].as_str() return fmt.format(r, t.strip()).strip() else: - return u'' + return '' def the_template_func(self, text): if not self.patterns: @@ -93,8 +91,8 @@ class ThePlugin(BeetsPlugin): for p in self.patterns: r = self.unthe(text, p) if r != text: + self._log.debug('\"{0}\" -> \"{1}\"', text, r) break - self._log.debug(u'\"{0}\" -> \"{1}\"', text, r) return r else: - return u'' + return '' diff --git a/libs/common/beetsplug/thumbnails.py b/libs/common/beetsplug/thumbnails.py index 04845e88..6bd9cbac 100644 --- a/libs/common/beetsplug/thumbnails.py +++ b/libs/common/beetsplug/thumbnails.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Bruno Cauet # @@ -19,7 +18,6 @@ This plugin is POSIX-only. Spec: standards.freedesktop.org/thumbnail-spec/latest/index.html """ -from __future__ import division, absolute_import, print_function from hashlib import md5 import os @@ -35,7 +33,6 @@ from beets.plugins import BeetsPlugin from beets.ui import Subcommand, decargs from beets import util from beets.util.artresizer import ArtResizer, get_im_version, get_pil_version -import six BASE_DIR = os.path.join(BaseDirectory.xdg_cache_home, "thumbnails") @@ -45,7 +42,7 @@ LARGE_DIR = util.bytestring_path(os.path.join(BASE_DIR, "large")) class ThumbnailsPlugin(BeetsPlugin): def __init__(self): - super(ThumbnailsPlugin, self).__init__() + super().__init__() self.config.add({ 'auto': True, 'force': False, @@ -58,15 +55,15 @@ class ThumbnailsPlugin(BeetsPlugin): def commands(self): thumbnails_command = Subcommand("thumbnails", - help=u"Create album thumbnails") + help="Create album thumbnails") thumbnails_command.parser.add_option( - u'-f', u'--force', + '-f', '--force', dest='force', action='store_true', default=False, - help=u'force regeneration of thumbnails deemed fine (existing & ' - u'recent enough)') + help='force regeneration of thumbnails deemed fine (existing & ' + 'recent enough)') thumbnails_command.parser.add_option( - u'--dolphin', dest='dolphin', action='store_true', default=False, - help=u"create Dolphin-compatible thumbnail information (for KDE)") + '--dolphin', dest='dolphin', action='store_true', default=False, + help="create Dolphin-compatible thumbnail information (for KDE)") thumbnails_command.func = self.process_query return [thumbnails_command] @@ -85,8 +82,8 @@ class ThumbnailsPlugin(BeetsPlugin): - detect whether we'll use GIO or Python to get URIs """ if not ArtResizer.shared.local: - self._log.warning(u"No local image resizing capabilities, " - u"cannot generate thumbnails") + self._log.warning("No local image resizing capabilities, " + "cannot generate thumbnails") return False for dir in (NORMAL_DIR, LARGE_DIR): @@ -100,12 +97,12 @@ class ThumbnailsPlugin(BeetsPlugin): assert get_pil_version() # since we're local self.write_metadata = write_metadata_pil tool = "PIL" - self._log.debug(u"using {0} to write metadata", tool) + self._log.debug("using {0} to write metadata", tool) uri_getter = GioURI() if not uri_getter.available: uri_getter = PathlibURI() - self._log.debug(u"using {0.name} to compute URIs", uri_getter) + self._log.debug("using {0.name} to compute URIs", uri_getter) self.get_uri = uri_getter.uri return True @@ -113,9 +110,9 @@ class ThumbnailsPlugin(BeetsPlugin): def process_album(self, album): """Produce thumbnails for the album folder. """ - self._log.debug(u'generating thumbnail for {0}', album) + self._log.debug('generating thumbnail for {0}', album) if not album.artpath: - self._log.info(u'album {0} has no art', album) + self._log.info('album {0} has no art', album) return if self.config['dolphin']: @@ -123,7 +120,7 @@ class ThumbnailsPlugin(BeetsPlugin): size = ArtResizer.shared.get_size(album.artpath) if not size: - self._log.warning(u'problem getting the picture size for {0}', + self._log.warning('problem getting the picture size for {0}', album.artpath) return @@ -133,9 +130,9 @@ class ThumbnailsPlugin(BeetsPlugin): wrote &= self.make_cover_thumbnail(album, 128, NORMAL_DIR) if wrote: - self._log.info(u'wrote thumbnail for {0}', album) + self._log.info('wrote thumbnail for {0}', album) else: - self._log.info(u'nothing to do for {0}', album) + self._log.info('nothing to do for {0}', album) def make_cover_thumbnail(self, album, size, target_dir): """Make a thumbnail of given size for `album` and put it in @@ -146,11 +143,11 @@ class ThumbnailsPlugin(BeetsPlugin): if os.path.exists(target) and \ os.stat(target).st_mtime > os.stat(album.artpath).st_mtime: if self.config['force']: - self._log.debug(u"found a suitable {1}x{1} thumbnail for {0}, " - u"forcing regeneration", album, size) + self._log.debug("found a suitable {1}x{1} thumbnail for {0}, " + "forcing regeneration", album, size) else: - self._log.debug(u"{1}x{1} thumbnail for {0} exists and is " - u"recent enough", album, size) + self._log.debug("{1}x{1} thumbnail for {0} exists and is " + "recent enough", album, size) return False resized = ArtResizer.shared.resize(size, album.artpath, util.syspath(target)) @@ -160,23 +157,23 @@ class ThumbnailsPlugin(BeetsPlugin): def thumbnail_file_name(self, path): """Compute the thumbnail file name - See http://standards.freedesktop.org/thumbnail-spec/latest/x227.html + See https://standards.freedesktop.org/thumbnail-spec/latest/x227.html """ uri = self.get_uri(path) hash = md5(uri.encode('utf-8')).hexdigest() - return util.bytestring_path("{0}.png".format(hash)) + return util.bytestring_path(f"{hash}.png") def add_tags(self, album, image_path): """Write required metadata to the thumbnail - See http://standards.freedesktop.org/thumbnail-spec/latest/x142.html + See https://standards.freedesktop.org/thumbnail-spec/latest/x142.html """ mtime = os.stat(album.artpath).st_mtime metadata = {"Thumb::URI": self.get_uri(album.artpath), - "Thumb::MTime": six.text_type(mtime)} + "Thumb::MTime": str(mtime)} try: self.write_metadata(image_path, metadata) except Exception: - self._log.exception(u"could not write metadata to {0}", + self._log.exception("could not write metadata to {0}", util.displayable_path(image_path)) def make_dolphin_cover_thumbnail(self, album): @@ -186,9 +183,9 @@ class ThumbnailsPlugin(BeetsPlugin): artfile = os.path.split(album.artpath)[1] with open(outfilename, 'w') as f: f.write('[Desktop Entry]\n') - f.write('Icon=./{0}'.format(artfile.decode('utf-8'))) + f.write('Icon=./{}'.format(artfile.decode('utf-8'))) f.close() - self._log.debug(u"Wrote file {0}", util.displayable_path(outfilename)) + self._log.debug("Wrote file {0}", util.displayable_path(outfilename)) def write_metadata_im(file, metadata): @@ -211,7 +208,7 @@ def write_metadata_pil(file, metadata): return True -class URIGetter(object): +class URIGetter: available = False name = "Abstract base" @@ -224,7 +221,7 @@ class PathlibURI(URIGetter): name = "Python Pathlib" def uri(self, path): - return PurePosixPath(path).as_uri() + return PurePosixPath(util.py3_path(path)).as_uri() def copy_c_string(c_string): @@ -269,7 +266,7 @@ class GioURI(URIGetter): def uri(self, path): g_file_ptr = self.libgio.g_file_new_for_path(path) if not g_file_ptr: - raise RuntimeError(u"No gfile pointer received for {0}".format( + raise RuntimeError("No gfile pointer received for {}".format( util.displayable_path(path))) try: @@ -278,8 +275,8 @@ class GioURI(URIGetter): self.libgio.g_object_unref(g_file_ptr) if not uri_ptr: self.libgio.g_free(uri_ptr) - raise RuntimeError(u"No URI received from the gfile pointer for " - u"{0}".format(util.displayable_path(path))) + raise RuntimeError("No URI received from the gfile pointer for " + "{}".format(util.displayable_path(path))) try: uri = copy_c_string(uri_ptr) @@ -290,5 +287,5 @@ class GioURI(URIGetter): return uri.decode(util._fsencoding()) except UnicodeDecodeError: raise RuntimeError( - "Could not decode filename from GIO: {!r}".format(uri) + f"Could not decode filename from GIO: {uri!r}" ) diff --git a/libs/common/beetsplug/types.py b/libs/common/beetsplug/types.py index 0c078881..930d5e86 100644 --- a/libs/common/beetsplug/types.py +++ b/libs/common/beetsplug/types.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Thomas Scholtes. # @@ -13,11 +12,10 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets.dbcore import types -from beets.util.confit import ConfigValueError +from confuse import ConfigValueError from beets import library @@ -47,6 +45,6 @@ class TypesPlugin(BeetsPlugin): mytypes[key] = library.DateType() else: raise ConfigValueError( - u"unknown type '{0}' for the '{1}' field" + "unknown type '{}' for the '{}' field" .format(value, key)) return mytypes diff --git a/libs/common/beetsplug/unimported.py b/libs/common/beetsplug/unimported.py new file mode 100644 index 00000000..7714ec83 --- /dev/null +++ b/libs/common/beetsplug/unimported.py @@ -0,0 +1,68 @@ +# This file is part of beets. +# Copyright 2019, Joris Jensen +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +""" +List all files in the library folder which are not listed in the + beets library database, including art files +""" + +import os + +from beets import util +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand, print_ + +__author__ = 'https://github.com/MrNuggelz' + + +class Unimported(BeetsPlugin): + + def __init__(self): + super().__init__() + self.config.add( + { + 'ignore_extensions': [] + } + ) + + def commands(self): + def print_unimported(lib, opts, args): + ignore_exts = [ + ('.' + x).encode() + for x in self.config["ignore_extensions"].as_str_seq() + ] + ignore_dirs = [ + os.path.join(lib.directory, x.encode()) + for x in self.config["ignore_subdirectories"].as_str_seq() + ] + in_folder = { + os.path.join(r, file) + for r, d, f in os.walk(lib.directory) + for file in f + if not any( + [file.endswith(ext) for ext in ignore_exts] + + [r in ignore_dirs] + ) + } + in_library = {x.path for x in lib.items()} + art_files = {x.artpath for x in lib.albums()} + for f in in_folder - in_library - art_files: + print_(util.displayable_path(f)) + + unimported = Subcommand( + 'unimported', + help='list all files in the library folder which are not listed' + ' in the beets library database') + unimported.func = print_unimported + return [unimported] diff --git a/libs/common/beetsplug/web/__init__.py b/libs/common/beetsplug/web/__init__.py index 3cf43ed5..240126e9 100644 --- a/libs/common/beetsplug/web/__init__.py +++ b/libs/common/beetsplug/web/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Adrian Sampson. # @@ -14,14 +13,13 @@ # included in all copies or substantial portions of the Software. """A Web interface to beets.""" -from __future__ import division, absolute_import, print_function from beets.plugins import BeetsPlugin from beets import ui from beets import util import beets.library import flask -from flask import g +from flask import g, jsonify from werkzeug.routing import BaseConverter, PathConverter import os from unidecode import unidecode @@ -59,7 +57,10 @@ def _rep(obj, expand=False): return out elif isinstance(obj, beets.library.Album): - del out['artpath'] + if app.config.get('INCLUDE_PATHS', False): + out['artpath'] = util.displayable_path(out['artpath']) + else: + del out['artpath'] if expand: out['items'] = [_rep(item) for item in obj.items()] return out @@ -91,7 +92,20 @@ def is_expand(): return flask.request.args.get('expand') is not None -def resource(name): +def is_delete(): + """Returns whether the current delete request should remove the selected + files. + """ + + return flask.request.args.get('delete') is not None + + +def get_method(): + """Returns the HTTP method of the current request.""" + return flask.request.method + + +def resource(name, patchable=False): """Decorates a function to handle RESTful HTTP requests for a resource. """ def make_responder(retriever): @@ -99,34 +113,98 @@ def resource(name): entities = [retriever(id) for id in ids] entities = [entity for entity in entities if entity] - if len(entities) == 1: - return flask.jsonify(_rep(entities[0], expand=is_expand())) - elif entities: - return app.response_class( - json_generator(entities, root=name), - mimetype='application/json' - ) + if get_method() == "DELETE": + + if app.config.get('READONLY', True): + return flask.abort(405) + + for entity in entities: + entity.remove(delete=is_delete()) + + return flask.make_response(jsonify({'deleted': True}), 200) + + elif get_method() == "PATCH" and patchable: + if app.config.get('READONLY', True): + return flask.abort(405) + + for entity in entities: + entity.update(flask.request.get_json()) + entity.try_sync(True, False) # write, don't move + + if len(entities) == 1: + return flask.jsonify(_rep(entities[0], expand=is_expand())) + elif entities: + return app.response_class( + json_generator(entities, root=name), + mimetype='application/json' + ) + + elif get_method() == "GET": + if len(entities) == 1: + return flask.jsonify(_rep(entities[0], expand=is_expand())) + elif entities: + return app.response_class( + json_generator(entities, root=name), + mimetype='application/json' + ) + else: + return flask.abort(404) + else: - return flask.abort(404) - responder.__name__ = 'get_{0}'.format(name) + return flask.abort(405) + + responder.__name__ = f'get_{name}' + return responder return make_responder -def resource_query(name): +def resource_query(name, patchable=False): """Decorates a function to handle RESTful HTTP queries for resources. """ def make_responder(query_func): def responder(queries): - return app.response_class( - json_generator( - query_func(queries), - root='results', expand=is_expand() - ), - mimetype='application/json' - ) - responder.__name__ = 'query_{0}'.format(name) + entities = query_func(queries) + + if get_method() == "DELETE": + + if app.config.get('READONLY', True): + return flask.abort(405) + + for entity in entities: + entity.remove(delete=is_delete()) + + return flask.make_response(jsonify({'deleted': True}), 200) + + elif get_method() == "PATCH" and patchable: + if app.config.get('READONLY', True): + return flask.abort(405) + + for entity in entities: + entity.update(flask.request.get_json()) + entity.try_sync(True, False) # write, don't move + + return app.response_class( + json_generator(entities, root=name), + mimetype='application/json' + ) + + elif get_method() == "GET": + return app.response_class( + json_generator( + entities, + root='results', expand=is_expand() + ), + mimetype='application/json' + ) + + else: + return flask.abort(405) + + responder.__name__ = f'query_{name}' + return responder + return make_responder @@ -140,7 +218,7 @@ def resource_list(name): json_generator(list_all(), root=name, expand=is_expand()), mimetype='application/json' ) - responder.__name__ = 'all_{0}'.format(name) + responder.__name__ = f'all_{name}' return responder return make_responder @@ -150,7 +228,7 @@ def _get_unique_table_field_values(model, field, sort_field): if field not in model.all_keys() or sort_field not in model.all_keys(): raise KeyError with g.lib.transaction() as tx: - rows = tx.query('SELECT DISTINCT "{0}" FROM "{1}" ORDER BY "{2}"' + rows = tx.query('SELECT DISTINCT "{}" FROM "{}" ORDER BY "{}"' .format(field, model._table, sort_field)) return [row[0] for row in rows] @@ -169,7 +247,7 @@ class IdListConverter(BaseConverter): return ids def to_url(self, value): - return ','.join(value) + return ','.join(str(v) for v in value) class QueryConverter(PathConverter): @@ -177,10 +255,13 @@ class QueryConverter(PathConverter): """ def to_python(self, value): - return value.split('/') + queries = value.split('/') + """Do not do path substitution on regex value tests""" + return [query if '::' in query else query.replace('\\', os.sep) + for query in queries] def to_url(self, value): - return ','.join(value) + return ','.join([v.replace(os.sep, '\\') for v in value]) class EverythingConverter(PathConverter): @@ -202,8 +283,8 @@ def before_request(): # Items. -@app.route('/item/') -@resource('items') +@app.route('/item/', methods=["GET", "DELETE", "PATCH"]) +@resource('items', patchable=True) def get_item(id): return g.lib.get_item(id) @@ -249,8 +330,8 @@ def item_file(item_id): return response -@app.route('/item/query/') -@resource_query('items') +@app.route('/item/query/', methods=["GET", "DELETE", "PATCH"]) +@resource_query('items', patchable=True) def item_query(queries): return g.lib.items(queries) @@ -278,7 +359,7 @@ def item_unique_field_values(key): # Albums. -@app.route('/album/') +@app.route('/album/', methods=["GET", "DELETE"]) @resource('albums') def get_album(id): return g.lib.get_album(id) @@ -291,7 +372,7 @@ def all_albums(): return g.lib.albums() -@app.route('/album/query/') +@app.route('/album/query/', methods=["GET", "DELETE"]) @resource_query('albums') def album_query(queries): return g.lib.albums(queries) @@ -351,20 +432,21 @@ def home(): class WebPlugin(BeetsPlugin): def __init__(self): - super(WebPlugin, self).__init__() + super().__init__() self.config.add({ - 'host': u'127.0.0.1', + 'host': '127.0.0.1', 'port': 8337, 'cors': '', 'cors_supports_credentials': False, 'reverse_proxy': False, 'include_paths': False, + 'readonly': True, }) def commands(self): - cmd = ui.Subcommand('web', help=u'start a Web interface') - cmd.parser.add_option(u'-d', u'--debug', action='store_true', - default=False, help=u'debug mode') + cmd = ui.Subcommand('web', help='start a Web interface') + cmd.parser.add_option('-d', '--debug', action='store_true', + default=False, help='debug mode') def func(lib, opts, args): args = ui.decargs(args) @@ -378,12 +460,13 @@ class WebPlugin(BeetsPlugin): app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False app.config['INCLUDE_PATHS'] = self.config['include_paths'] + app.config['READONLY'] = self.config['readonly'] # Enable CORS if required. if self.config['cors']: - self._log.info(u'Enabling CORS with origin: {0}', + self._log.info('Enabling CORS with origin: {0}', self.config['cors']) - from flask.ext.cors import CORS + from flask_cors import CORS app.config['CORS_ALLOW_HEADERS'] = "Content-Type" app.config['CORS_RESOURCES'] = { r"/*": {"origins": self.config['cors'].get(str)} @@ -407,7 +490,7 @@ class WebPlugin(BeetsPlugin): return [cmd] -class ReverseProxied(object): +class ReverseProxied: '''Wrap the application in this middleware and configure the front-end server to add these headers, to let you quietly bind this to a URL other than / and to an HTTP scheme that is diff --git a/libs/common/beetsplug/web/static/beets.js b/libs/common/beetsplug/web/static/beets.js index 51985c18..97af7011 100644 --- a/libs/common/beetsplug/web/static/beets.js +++ b/libs/common/beetsplug/web/static/beets.js @@ -129,7 +129,7 @@ $.fn.player = function(debug) { // Simple selection disable for jQuery. // Cut-and-paste from: -// http://stackoverflow.com/questions/2700000 +// https://stackoverflow.com/questions/2700000 $.fn.disableSelection = function() { $(this).attr('unselectable', 'on') .css('-moz-user-select', 'none') diff --git a/libs/common/beetsplug/zero.py b/libs/common/beetsplug/zero.py index 022c2c72..f05b1b5a 100644 --- a/libs/common/beetsplug/zero.py +++ b/libs/common/beetsplug/zero.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # This file is part of beets. # Copyright 2016, Blemjhoo Tezoulbr . # @@ -15,23 +14,21 @@ """ Clears tag fields in media files.""" -from __future__ import division, absolute_import, print_function -import six import re from beets.plugins import BeetsPlugin -from beets.mediafile import MediaFile +from mediafile import MediaFile from beets.importer import action from beets.ui import Subcommand, decargs, input_yn -from beets.util import confit +import confuse __author__ = 'baobab@heresiarch.info' class ZeroPlugin(BeetsPlugin): def __init__(self): - super(ZeroPlugin, self).__init__() + super().__init__() self.register_listener('write', self.write_event) self.register_listener('import_task_choice', @@ -56,7 +53,7 @@ class ZeroPlugin(BeetsPlugin): """ if self.config['fields'] and self.config['keep_fields']: self._log.warning( - u'cannot blacklist and whitelist at the same time' + 'cannot blacklist and whitelist at the same time' ) # Blacklist mode. elif self.config['fields']: @@ -75,7 +72,7 @@ class ZeroPlugin(BeetsPlugin): def zero_fields(lib, opts, args): if not decargs(args) and not input_yn( - u"Remove fields for all items? (Y/n)", + "Remove fields for all items? (Y/n)", True): return for item in lib.items(decargs(args)): @@ -89,22 +86,22 @@ class ZeroPlugin(BeetsPlugin): Do some sanity checks then compile the regexes. """ if field not in MediaFile.fields(): - self._log.error(u'invalid field: {0}', field) + self._log.error('invalid field: {0}', field) elif field in ('id', 'path', 'album_id'): - self._log.warning(u'field \'{0}\' ignored, zeroing ' - u'it would be dangerous', field) + self._log.warning('field \'{0}\' ignored, zeroing ' + 'it would be dangerous', field) else: try: for pattern in self.config[field].as_str_seq(): prog = re.compile(pattern, re.IGNORECASE) self.fields_to_progs.setdefault(field, []).append(prog) - except confit.NotFoundError: + except confuse.NotFoundError: # Matches everything self.fields_to_progs[field] = [] def import_task_choice_event(self, session, task): if task.choice_flag == action.ASIS and not self.warned: - self._log.warning(u'cannot zero in \"as-is\" mode') + self._log.warning('cannot zero in \"as-is\" mode') self.warned = True # TODO request write in as-is mode @@ -122,7 +119,7 @@ class ZeroPlugin(BeetsPlugin): fields_set = False if not self.fields_to_progs: - self._log.warning(u'no fields, nothing to do') + self._log.warning('no fields, nothing to do') return False for field, progs in self.fields_to_progs.items(): @@ -135,7 +132,7 @@ class ZeroPlugin(BeetsPlugin): if match: fields_set = True - self._log.debug(u'{0}: {1} -> None', field, value) + self._log.debug('{0}: {1} -> None', field, value) tags[field] = None if self.config['update_database']: item[field] = None @@ -158,6 +155,6 @@ def _match_progs(value, progs): if not progs: return True for prog in progs: - if prog.search(six.text_type(value)): + if prog.search(str(value)): return True return False diff --git a/libs/common/bin/beet.exe b/libs/common/bin/beet.exe index e91e175e2c7ba0e2a22ffbaa812e89f6220437db..742e1c19ef89536fbe6732bc8353ecc3f8cf05fe 100644 GIT binary patch literal 108369 zcmeFadw5jU)%ZWjWXKQ_P7p@IO-Bic#!G0tBo5RJ%;*`JC{}2xf}+8Qib}(bU_}i* zNt@v~ed)#4zP;$%+PC)dzP-K@u*HN(5-vi(8(ykWyqs}B0W}HN^ZTrQW|Da6`@GNh z?;nrOIeVXdS$plZ*IsMwwRUQ*Tjz4ST&_I+w{4fJg{Suk zDk#k~{i~yk?|JX1Bd28lkG=4tDesa#KJ3?1I@I&=Dc@7ibyGgz`N6)QPkD>ydq35t zw5a^YGUb1mdHz5>zj9mcQfc#FjbLurNVL)nYxs88p%GSZYD=wU2mVCNzLw{@99Q)S$;kf8bu9yca(9kvVm9ml^vrR!I-q`G>GNZ^tcvmFj1Tw`fDZD% z5W|pvewS(+{hSy`MGklppb3cC_!< z@h|$MW%{fb(kD6pOP~L^oj#w3zJ~Vs2kG-#R!FALiJ3n2#KKaqo`{tee@!>``%TYZ zAvWDSs+)%@UX7YtqsdvvwN2d-bF206snTti-qaeKWO__hZf7u%6VXC1N9?vp8HGbt z$J5=q87r;S&34^f$e4|1{5Q7m80e=&PpmHW&kxQE&JTVy_%+?!PrubsGZjsG&H_mA zQ+};HYAVAOZ$}fiR9ee5mn&%QXlmtKAw{$wwpraLZCf`f17340_E;ehEotl68O}?z z_Fyo%={Uuj?4YI}4_CCBFIkf)7FE?&m*#BB1OGwurHJ`#$n3Cu6PQBtS>5cm-c_yd zm7$&vBt6p082K;-_NUj{k+KuI`&jBbOy5(mhdgt;_4`wte(4luajXgG4i5JF>$9DH zLuPx#d`UNVTE7`D<#$S>tLTmKF}kZpFmlFe?$sV{v-Y20jP$OX&jnkAUs(V7XVtyb zD?14U)*?`&hGB*eDs)t|y2JbRvVO)oJ=15@?4VCZW>wIq(@~Mrk@WIydI@Ul!>+o3 z=M=Kzo*MI=be*)8{ISB{9>(!J__N-a=8R&n#W%-gTYRcuDCpB^^s3~-GP@@5&-(G& zdQS_V>w;D8SV2wM8)U9HoOaik`_z>Ep^Rpe3rnjb<}(rV`tpdmg4g@>h`BF#WAKLH zqTs?sEDwi<=6_WPwY&oS9!h@ge4(br)-Q{|OY*#YAspuHyx;~|kASS3FIH@oGSl?L zvQoe8yKukD)zqprHiFKlW%;G=hwx4l;FI%8m&(#zU|j&_bW@ThNpr9D0V}xa)%aIb zI$i2CA2mPU{0nJmK0dxe)dY-`z>ln($ z;r!UXuLDDi42|Zd3Erx&m8GqlFWbIX0V<*Gn6lVNq%gD>gw}da}r}ZQB~ns?p8uy4i0%1Ti$Vt|~OUth4=+yEmPu8{3(w zUDkd@?w?`_J9HBkx&ZF8v{+9phcT@3J8VI~wN7Ez)oJS6^dhb2N;;{RTXB`K*E$64 z3rDqRtY&&*}9yq2oUcvD7K)=@bWqC1X%l0jk)W<5-WBYC(#rn4H5)gp#eHMmwlLJq=^%|*gMQ*pq4VV(QhHA4CGj<;!d8i*#Z8CaN#*>VcCnj~;kkeUa{LUoKxFCaoQ) z(Lz++&x3Lwz;=6UnhwM!MvN17>{Qmb?dwgsTmzkLB~jD#wiGz73hc0bFE|C9KA#|= zH}%FQ>c&Y5z*TJD-<$$Y*WZx>5NNe-E-TfAt1!)%Wc@I;ZuNwxDGGasDIMyUNiVvG zq;Q70PYHcLO=Xgv2698@cJrkun-^>P2}|fMHlm7xaZmE<{&cQtb`{N9zj0bRmpW^T zzQV7oTs0ENHe&mxQ6DI7qd0SU4;3o*2qRd`X1>(=ew})X5Dx zx$lyzZM^emtdsbk^u+xwdSX$lp7h*2CkHCqDohShL)V4hM9k+UQLP(GN-H7!C8gyq zex`xuPQ(!g4}S>0r+CyH+xIAMP9Z&+?BT1!*kA<}dqRn*FwJPGe}l-sw(lGYN1b8} zWQQjQN`9tdtF?#aqMN?wu4E3)qGxzOhwr*vb;kX_%&U*-=KLr0raiGc^x8|=Wqt`N z?L0luR(~BF;DS@~yKDN7|*TJkj*-B%s1{65$`jY_(C#P&^rVi0?Ro4iaFbR)Z2NLxS0 zTL;%Kt22(A8JiL`U$i!iR&zLxx^E%H=*c-=+h@sisygu-_#m4J4LQqB?~vXvP4@yQo0-^oki(PiH+=FZl}&W)S-qI zk>W;2Zl-vl6rbe4X6feZb)l-Mv2oh^5t8q5@(Y-SPoUZ;N<5Tdl!h|=x!1}5)E;}=RcAXJ8(<$^13IV==^rU>wwq$hX3V4iuA0>h< zuxK^)myr=p7a)oeZ+g4u^9(OmpFl8J@{{UJfy=DjAf8lTTD00iSF3Kb9|GdM-PQp)0<* zZkW*V-TPpIXEKDks>&FQ?qoV&Tfa*;TJyB^yJa8xcch+*-cYj6E7HdBX!5)TIXSNM z4C2L57KVd0rioelfI{ELMrb&Y}?h%mk5iSTXrmJ zwlk6qsS{}3<}Uc!G}Wr;Tek1Tym8$SrWokvCzU(FVIAWTEa1pwE zBJ6JdS@$4RFBV*~g^Eo9MAFafx2rt|uRsR%xpNVyj8!g>2u0v=>eO zS~4nHBgR%cVxB-_OwP@%JN(CpY3qHvqsbt-TUGivY2Dr$b+=`6PJSkbWF)!Jn=iZJ zMt}mOG~-m{)L*SV+yRH!c@XR%)K^BqVRh zq&wib)2#d0V3BD*|F5o2J6$vbdJGh`O-30SrMI;e*Y&m8c0Bi^cD-$Daq1haK*i4o zS^0dLE!U;Du-W5i&*6##L30bjy7q7@lQPyCc8<%{>0)|vQlrFG_D_+v^1uh+p+bhA?!)dFEqi$(hoT?=hJt20DQXmOiJ``9LY)@=HE zO1esvSjV70vmITir9t{Om5D&<%?UTa#`5Sp-x@^?6JCK@(Y_-+ye_agHcB_zSUEYe zay}#@o~N5_?G>%q2t<~g3s!Y+G*Mj=P3Zn>mA2=HCm`lzap|)*f|(31R{)36WvAyz zfea$wK&B|2YxO{n>twI{fk3f0YVK4T;XDy#cUe=*$V6#=30zz**pkdJOUUdHcyGKx z={=%tU83}-sM&@LFz=EaBy8m5*VS4ZYhB<>lI{BnIk4cD&H_E|%!spiL(( z$1W0V$;KX^P(?<}XYHqoplpQo7H>!m)d{bdPaLde+h7(tf+ZB(6MxWZnoX6&>|)(q z*DB~wjMmL&u~F-ZIbJ>BJ5ZM6ik)gUbdlBM`Quqove#M~lf*ebB4nBg}NN8q8e!? zVj>HOMJZ@LQzOdvHUSih8gCt%IxvyHLmO^Ea(*!Nd-Zuw>`f87{SkAwbrcIp6hiff zt7^x@FVoBVwDl9eTxT2$))(-5-O9W=qunp;*yvYT{VJ=~FI-x;pN&=5ArA%W0()Z} z=?f87g#Y@j2_ct@T|gzY^?R)mq?NdksZ}7gJW^{18>hCuy{s)%iDWGzC?-DRKLl?l zlnO5zQf3*!v6nJ;)xm`Sjm!6zf=o%-07p#e5?cL}gBtB`Nq!dTtt@<7#(o8m8xm*XOvN65AL(=C_D} zJM9UyYteSSwriu8{DkKl6tSk&09e8kMrjh@N|SS;@9l|6^W@_Q=i{`@$NUzI6|VF> zN{Rev95oVSa&%)ew#+uKZf{3cFg?f64ASokLt$^COgO2#BW71L>H7~o2Zg;=Z|nCM zZ=N18^ET^uY+VpF$K*teqc&2xaTF!LhIKrwGne_WBX+B_9vi@rt2GKHy|kQxSUJ18@{fEswY{>va~$3%JGyYfr29k%@bck16c zdf9Hh?|r@PC`@3R-j=#7868z@m3)O|u0`Iw|bd&(6~U$UMGD@Vncn>Lm}{NqU9US&{gYu`~lU+m1n zi1g$#vC1#v|9B;ObTzhRor!#90$^5b(Gy`buihHrRfjV>-l^6#?Dg3lZ}@PRD|I(> zVcp1Kiyr8xABHMWk$xp&hFzvUhIKbDi1339ve8Ac5ON73NDM}^^I8O?+8zk+GVA0S zG|7G=o9JQQO;-x!z=zz5c@^<{-AWi)tG`b65v40t#CwnzKA}>?+z|q4`eNlNfRXZK%L4$WHQ)8Sgo0 zwE~@9)+4fUIf8fW?9TihJ6Hgttrta)MqB{FTBqxu|CDLzEKWn{Cn*>&wx$DtvzSvC z(4Jr-g8~qe!NL-;BVhBlx}Y;!It5;VT~^q_HdZcH!a^(MA3%zpy!zmpD(NfkvF=9= z6p^lmDSFnrRVn4npverH%%I5(CT}SgTNGB)0sCY%@`7%@lG#4Gt*2;3c3;0E8(QyS zoo-l-h2)DEIh-3t!@^Gefe~>Aq|Sbf{goW=Op7FDAB-5amdpAhatG_BQh1V>p|DF2 zoM~XblmiX(kl0U_veatKBQ+uz9@Z1{N|y`0j<11Sd^JtI@w2S`$mW?%;MWLc4%=HL zi!p2d7Nf9k{=Kw;xt19k$vh+UMEX9C2D?jRP0wn3ihvj zIKqjR_QyB+t|%#l=^@PkY$HlM{<4z$Jve9n{#ZUhYv#%_q#uJnen z7S7e0{d|oCJ_u>EJ_(yUqk*m3cisoGsENRi9?F=l*A~&-*(<$4vm*-sUaFT_dJdnX zrOQM7ERMPl>SbN2|4`NV9yZ$|0jqv#7_|5qM&SK>FdA$Qn}>sahte?IEg|!hNZ-Lw z+2M47yawJ6YgZhmd7`)o7cpN%77HvCf^&@h2FBhy;L2rI>K+Cp6&?pq zlFhyiSR(126>L@rL1c*79q1?uBeI5<%2ZP3K!*8bJ8n5Vkdy&9Re{a#rI- z6fv$Y@#|&(1pg>!eIKW$IeEqD_akO!YCNey`?q5Uh$a^MgG!T#n1>V}I*O@Oh-I-5 z%k{Du%Iw6?)MXzjh?<)@`1%M|Z2fN100q^u)YBKp;(8NX!a7BpNWL}bB60|{!@3IM z&!_-j!}^5^fVs3)8n2d}7M6&L95t6HGcO7O>k8tJiY2gy{mtC0V*s z;mM4hWAvYlP0?$+)i!p-gT`AH%yAiSovz=pXFBCU*-y1#y_wmwf!PgMrEDEyp_Y+h-3$ZW$Ny$8H)g+M&odOm3D+qCuDCyTVF4s8_v zmEyLRLz)cEXCoqszT`H8*!|T3k)9}efv(zxR?xmMPtJ#z>B&Eo77PE!jE`0XJbxM^ zJEbz?Lu5g--#l!-Y#gzXP3G6p>XOps?99>9SjC=T%MY0{>#J9bVPGK(CmAlr@LDVu zdtE8Cwy$lsu#8`O8L={lK%5}c`pb6GjOmh$5gX((WMNF8jU#kU?6HQLb+0+w?hE$3nE@wxIvFA6~zB7QMVyoEeHQuBH-S!>tRw89F zyIi51ALX;4mfyl>Gbw7NUa`Y^`9s-NepV{j;n;E-$Ceyj?qimR?nQpJ7Zt@YCfL5$ zX%(74|FeDDa8Ol;N-078H81eqW|LX(_9$cc`%a*!#=7{V2=)|lNG5a40)v6g4t z01XUUv68UZ2|@vkl?ceW7{YVw!nCy? z+sAnJ?mvd`Ab`J#GpRgV_N#doE}<~&Z?VHb%c3L;ua)NW2qzfhmeh>}dH zGKiE|U&0iVSyyQ$NO;+GkhAqI3{1v-UXl6k&ogShm<+H}bDWf8ZLbv`!7=F`^V*WW z%|fH`g0dA}vmj?dt{;}&QQW)P9h)H{A4EQ&PP7V>>J53l4KOcs^mIW( zWkEdG-lC&N1l;w9;87FIEh#42)wpNXA?u;BStwK2f%x9dIa=c%`6v*^^D7Rdeo3P2 zK9dB;uN>7oyTltCA%$60W`E3W-dBpg zuqcq@x{}^i&v~(2yR)n>8M=s-@@eAy%xR>v4&Y%h*z7^|kj=+ut-*SgnXpUQ2Za%i zw_32)!m77h`9S6v$7W)#c5Gu%xh%>rSYMFAD@|Kh-5MzR0ebF=8}-^F_#pg>cMe^Q z_fFTrqJD?X&Jg+pQE^7T9S;~YZ`N{LIq@lM=%?CSV`D_iRT3c{J=yaikxU5%rHT=TI9ln9_p;9*QY6sX)@dJei;QU6QC|w1dx9PPU z-k*1jcMjN$eZXl0=c@we30H5Z#G4Zf18#{O`?4|fubhbI#LpT6?u0J@S5*J&gl|g| zx>4w6bp!F}L5Qb)5yTF=Q~b_2auNe$u2af-1--x-Y8ugJ)$~A7xqyDQUb~z9yjp?2 zS$2CCh3xpcnb+1EDhBdlycVY?TH-GQhOBi1Em;xS%mih!zz5d%5ZTK)kgI(;YVM1) z9Y?6R=*3Ee3NQqA=9m}0tBfPY>WV^F{KDkb!>u=FvBx{<@$4HF#Ty?(D_|c16@7ar z?3sMj4pkIxD3B@pYY^(UW7-_E@LkG|E4F$T>^}02mQUF3kyHzn_+N+p{xB`ffEMeA9vW5-D%{ zZltI*4Xan_uaQoJoSn85x~zjwdZGe`c|L&8DFe`!Uzz7`w0>!xulJ>+=37i-p5mR> zWl?vJ+1b|P3AuYhVyI7#LAPEYZ87i$tRpmE}@el^F1lN0erixJ1-N#3v0fp0!puf z11^VLsS9qh<=8A zl(KovC21r`^>K0LV;-uDR<&qv-K@mIx|7<^+mo|TDsK^_F=k^064`x9BFi|CeU^vI zA`v->wGlB>5s}S`2Vld*+LS4GWdW#Z9=Ld+EhF-ng5iU)X7A68`i# zO|AEyO~DJK*d*(2vK_TGJ;J(KCFF$1nt-h(v%kz8V%#2jMxD`gWt|!-@k5${77Q@!{4z;ze=7&BScC z{l96Ke7GeU{#P5P(1-)>pb!x>_limI(??L33;=E&UU`S^Xg(o6V~Xzp2+b869oyFB~+oK91m(zDG}-Ce|yro;clXhx0fm zqA!a1;w8|CgOIS{tHtHPM)Qnv&@IQrVjZ>Cz6}8;hEX6s#`+#jXAT>_&8rE)U3h@u(3Rj2wHPF8HLr_+u|u2h!@v|soMqnSEk8Zd`9UErc zRN_h>v@U-yBXM8Ej^Rk$+sR6^P!=M|4(TT&#@8NU-8`?Hjo1~wjxi#DFXslCbHj#H zR5!NB>1Vtka3nsdw|a3-Y^?Qbif>?ajCQZ}h|~?V$4;Z2hvePt!VjWV5kP_Mdzd#2 z(Ya9OE~}OG95vq%MZN6^iVy-|(zl&p4c#oK!g~#g9ul0wCtz5||XBmlcb|@y+~5^oMA2 z%2&t|Z30b#v!su;P0>oP@n%l!68gTFk*t&4-cTiC(g?CTh0XM*M_NA`XrI~P!(S-N zL`<-L&IbV?K2X3qpYwnLW)JqoQsvmwRaiiIOAWlUuFCW7CR}XuDqc-j>a`x<)1Wa~ zw1+(1-L|GuLWkn}HjH3W>Zkjq4e-!WA;hn0iSIXW`S*t~{JgUpYShtg%LoE=slzv~<=K*WA*ElMAxu<+e5ER>PXppG$|uZeA(Temu%&q(p;3AFN2!kq zm=?vfxfpqDEN!LF)Xm0H1wg{HMEXo-l13}ryyuWqH$7J>Xgp69ORBMSo%EOR{GE@T zp6`=69Ftb3=ONylwdwgfFVgK&D$mcnFSmVb{~?FB$0_H`z~O7eOlSLUCm#&_o;kIB z^GO&pU!)Lg-zm3^a<;FL4;!T`wb1X9I%}R0*ioufT+j91NaBu?NMeOwVtj_4-Bj0@ z_j+s0>1Gh!;oi!cvc4Mg&8Yc4=Cmj3w59_z5~=-$9!bpUA~dL*qwByWnz05DbT{~4 z*jZ@K?vDlzYTtT-qUP-5@^1W$cjLZ1m)7`wc?;yk#>sw)Ni$-;5OH_f-AMb*3BElL zTXVmwcEz1Nab&8Q-#V9uW2Z6VdwH||2KhpVBR4w8!{_^EvduYpj=@m1wadC|nCyj2 zt$A%;w3fp&nPJJ87ID86l?_lyq<-5M`#ZFGH^n*bFxrb{B4*!>glHD=IX zaR4E?rmXV`e=Jb3r)umy9O_=}HG_<;wLag>;c-u)&Cx(xabWC&VP!^jmFM&Ib z$EM)|j1Ueju0pu}b54-q=pis$~y&T*+xHtN5ij^Dv z^%7mNlKsbrMJuxz??mDQn__!^I>*gYDhiq>gCh>6y-yP!!np!os_nT!v)geY)f(H$ zMdxVz82saUVjQ{l!Fyx32g`P8jl0P*QX^tlU_Sb?kt&IuWuyvXIfW6 zvj(<2h5p+D2H`EwSwH=TECv*ISR}=U4K0jI?@X;}rSnDnja37_hg1U|)xdV^hSx;N zR_l)tW>JcPb8F@5C~uO{c@SQX_Wc-vx12+X_zdyQjX9DVg;djzhq7W0o z))<;YTY1Kqwi$lJ9G%8d#&=Y2g-5J9EDiLvQu;DVkGayNG;o{qwO{JmzR6Uh$UG@x zPCO=Jtf)bg*6_lp#3+w^Tg=a7c|p*fGtm(jE${gPmO7HD77SR?ytQ3_Bxr`(@-qAT zWfSOxaSdnVed(w}=&i-FC`!Pi=?<=yrTgx#ws#DU@R`1IyXR+k0R7~IY6mXQnIYJ=|Dqf4+{O?83Q*D35 zm~q?{FH`;v)-R{BFDCMi3*t-k>{7fQ)8nw?9TyWqG3`Ursw{KR7s%pMMe3iM)dT*M`1?|}%AZgc@ zX30+IPfbP!7X!AEjBUyvWF0|-nESBQh0Mtj(=rdU9mNVG#;RgmWP&-P(zBuAracc- zp+(j}^q7=iuyEi?+-C&NiI3TU^)U0@n#|Xx-UoNc*6NmU3HqR;Wl%dL zkIaY`kZ}eU*h+@_w{SA-$LNPRs?I`9&yRXRk~$gghBqUHqL4xmtMtVD2F!n`DBU&Y zA@L!Y3w6XoW)F{rN=O!R5%FX>|1Ypcy+BCeYqX6PttY}QV(d8A+D=AhCvAj2I9Ci+ zE_xz1LN~*Y8IN@_s1s-}DbcJjI5vpO#CDDjrv=T!AxN@1Y#t5bfti^9CyoyfXpL_T z2V8Sei{e7KzA*ct9Fu(Nld9;CL z?d=gOO0=h4Y+4Jb!Gh3(cScOi?2L8L!@ zXRz-XiI$JM!z1>gk%aITI}Ha2`#~+lD$VpAZrrCeDp|VeRi;hXLX+MU&wulyCi{V@ zp~_QZXJ}92zB_-Nbp#$k+W_m_M`OPZC+5?&W-o>zKXw6;Mw zPZVMo6>O;(y{(rJ))j>Jj--v{g0^&C9d>R#xu`p+I!;{+20Fvd@~tlHPH#Z}#D#80 zwJKsBYO=M&SD3rt(@+KWTkw{8Sk2`v+CyWht11NA9@xI&HVQx{ji8>XzDsLtBV)te zncQFSH2RmvZZP^+XpO58RW`&kpI(%5tDHnrJ71E)Kc>S>es<7(F(N@%94gfc zt}u%Qr8lQ*gBzd@RpP2l;SukoBN6k<1H@t7b$bS(TH|}1=7p2j`DH3Rgr=l(6PIL> zoLb8o5hMoHL6p-P+JoNWY5<8%Jy_)&dQZbMH@;n1k5gZVSDG59CRwN@mS3YieR+R+ zBAkSWPvs4(spUN{Y+l|!Sg;6&bFUYtQyI6H=HmrUtM0Jb+GO9GuVy+uB51tb7Yv*T zYFD3tL}TJ3oc#GNW=rR=aO>o4-~yYIy{l>KgSZEC^?)4Dv_{}AeTN7(PtHQSsCppR z-O&ueZ%;ojbgn0xqy?c1=D}`fMTVQ+(Hf7#GMidk%E4&NTj|ys)55Ur?JSdKcj|Q# z@lkkIq~gI09sUQhXE1Oi`1G%+0*FVX$zZ^K;H)*Biv-5nT~_VsJQLwR!63B8U?hW)?=-Hdlqq`a)%WG*cKqMfqu&U6`6B@bTa*hHb`MGTvKIJRjs3NL+*6oUu`f zPz-+a;yzVqgUnl|_Ft%7(MqVuf;hXE{lHCF2ZJV3dw8A0ZK9=1GTeu=CHDQBU?IYD zYb`v2rzovi+{2bQ@h4?87jd5uw$%IJMg@8LZ1vzM6o{&c7{V%n5d_#@0$C223kja0 zjv%e6ch#8!Yiyzet6(Ps>o6M6;8nan=LVmWkAUisOgL8(UDj`QAml+b0wtTWQz})) zSJ`rn{zz=D(Z4h{djmEwSX!(^ZPaMhTGKdHXyg77DUCNG*u3gne57pNGR1|dUZ|DD zUz|F?3wuqfM>2#Z)dh{pi{q#ASe1LBs*PR_05B!hk@A>Ki}d9}v5yvdfiOihrQ8wUSumgQPT z^#CeUufkXX@5DLrvx5#hRD)I=NS3K=5*W_V>qWl{rNnBGEPPs!nOv=RtGrjq3z|oz z%TQ`338%qxgAOAc(jbx<>pSsBsbK8L>)Xq6SeSZ@BwFdhWMPA9H$=OVZ%8pZ3SwOU zve7>|_N5K7hM2X<8_siH#wcItPcL%K1u0ta&UGs3R;U zDFUi^?@j0u_Vu&Ua)bjE8WCg%lxXp`R{m?P8%2g!!Sm&i8ysliZz-Pe)W~iKi$2@- z%_3*UuodHBQkRe`Gg%(oKyxZiY$9Kkf}%9HjO|Gs??vP=@Th3JlaO^YUi*R06`J)L zM<&jp6-PabbnTBvoEC@yMN~q%Hte32CG^+Hq!Y-3#Bck`o&Ye^n)8gAcjrS3G3;f# ztlv78_U$6c{iV}g2vq6cNn)6j5UD?NVll)n<{W@3DD~vmQD0afGzl}{o*aCRADki_ z=2bm;e{nE5XBgAp9!e}Kj3yT4)qV7PJvnnErUkw1#M->mWvgOe+8O_dh*2zSE)^88 zHm|BVM?!u%g)5yXB(SvQ%{h1(*lmIK`cKw|O268HNamNIhp(p3)}H)Y zPDp#QH5Ayq^3-4%J5cMD$!OkkaoPKe-}-JTT@VzuHovho{+xMvA)b$wYN|zTDK{_A z!=;ipwz8(>5Q?(SiryT8!!Lqar~p8UnO`j=uM&6I*a>7SB%*^ANS&jk`adDWz7Sx2zfof8}0FuZtes9;}u zB+1-Zal>$baBaxDuX&9iE1ln=o-T=^!RCgr5bsJ~CbW6gB=GQPFj?(4`p2#G(oAxe zKV8Tn{kWAQX$9i_OdFVjLG*L=sG>-tI9wRH1Q$&*H~5=?sf z00n0WnNK)qk3fD%dRC{TQE?y+baCD^r9)P~=SLLO6W>vFO;58*F`ox*%F>k6!x3eP zc{T1$&hc9d;0GDo(7-vRvd2`T@-mUcE?7|-H>ONK0Yq}-H>J~aChwpa{&C^2T`ni| zz*%QM45LVV0&)-tQ>Q{NTp92^7BAbrnT{X= z{9VAVs&sD53A%Sg-2258V;u3+r`FgO<8l;^HMYd#YmI#r=S~9KckScO`lDlr5YJ*H zTi?`7<`$KC)kJX=7tUgxcLwDBKwjd8!cf(cQor`?hg6AB>D0=FrBh?)RW8VhP1ByN z)SlFH0!LQ*%68G_C6fTCp&&2fem+vRBmRkKB$Xxc=k(;|r)@Y%0}Wnp#Qlu=W?q%I zCiOVHU(Drsu?a?sn+Gsw=b_S!Z^?s&q(`@$B9FqBJoJ#Xr)3nW#N~ydM4dP7PTb(t zlMfWb={ATW2Afk+3ssZm9Am&uE$q-@f_UMx1Dod;oX)$GpGoCu2*2&EynoQJ>*{3a zoZ^Vt6|5|YO|SfVPV8Lm$x+&q!JI(%%5kuSFHH)rbqC$g2l1>Ux5m8#4#{F8PY=8VI@V4ed8Ja-K;lqb{X!#!&;aj>ZKK?0ZXiqsqd&(KwQ!=z@*^8i? z#a%onx%!-sH_EUGHPGr3#5%U+M#`Q?w}Uk52@(;DP87;v74K_x_RR*0!>X&5ktlO# zmEzeP1rG74R6Zc)k)ZLcZFSRy+?rG@s)+duS#@ktn@C|03e3*a8spHy20vtI^`9bT z_u`f)O#Ei@b@NBgI_(O!s3JdE!u(*Tcut&)y=WsL6Nwiyyej-%DU2D=c!%rQ?BN9R zn<^_3*dgnGGaw`s2nTI<@3*@soU1iqFLm{L9%O65oe^%}+Em03Ncf~gPHAW7B|LXy z0XAoQ6Q0}EOJTxui@bz$6>16rPWHPuQ*dpY}NlQP&(W~Yj6k}hp_|woF2JBV+Dt3<`-hr%Ezr=pxxW7j1 zQwQya#XN8`!r~?-DhW$G7|LP$7=SE~H0T%rEt}55mQ81YbJ9bhyDkeI2OSDJDZ<&H zfCpc7z{})0@Nt=f179eoSpdWVRPk$8P4*5(N=#E;;=Ie`upgiM9uKzS z@x}&0gFt?wmMqhh0#=h0PTsd*lS2lcL+|pf>WYJ00cC2+LrF&Ku@*@=<3Z4k@6y#! z1HMbnm)Yt|r(a~xO`^ssNf!ar*|t-Y`Oe|QKy0%RQc&v8h?=9KfjzMc^aKlRn{_^f zPOx^2NbYUce~}0pm&&~$NzXK7ifEu4c5>-SK}EYd6hM6C<_M=<>z^`Oj3k*G7N#-` zxyvde%Z#-Cp}s%T3I@_;8$>*}*5a{_4bhZ5PS`}wwZ3Xg`+J=Nw~gilc5$!BBVGAY zD&t7Tcn~`6DR*<+%e&|>X3_gVDM4CAw(lkKjiS9|fHYi7ehib9a)?dYa0xv1kYhY| zK1s8QHID&!cPqsnt$usgt_PNiBC$i=EUeC-oJTG8+^^rP-j9@t9;JJwN>$ z4<-AaP5#qrU)yC(0;$ZBDYK-ka?;jB*)PXZ=Ze?K%?i!Ktb-ew40db_8Q7VV*EtTO zdUh6LWukK?5E%5p%-dPvF~TA|IkI*G{jrh8Wn3>JB}N<@nAM*td3w9`L)w-lniZ-u zc$M{GEz?Alj4g%}{#i}WSxk1qGl~wxM_gCa>p1@eM+n3+@v-S<(TCEr%<+pqQ7xQ? zGQ;jyC|j5B74kB3+(IwtKkA%G?O`f>Qqfnj3f7$OTvI!j;|gTIK$q6|JB8Jn9_vO0 z_@W-;zA>)&S=##f=tfTy!#_^$B-!k5xF6oc-c@rjBk6M~M|wHubj3;$=AMofQ<_AOs>}JJ5>u%(%)41kNIq1IvFKc1K))za8*eVg&hY`m|wpzYQxnde<~ z0>F0FV=72u2bV~!IPY^z3hyaE&K20W0xTUoB(F?-BcLgo=QC)WAQ$vR`^$PY!pZ4@cA({mL4nip57 zdCG^p;&{{ayb!lpWN|AY_dYVga-|DRmxFPw@mJ2*&FX8R`r5DPFlu7wmpdZSrh4hXG*R{@B@?OJgoIBda|NU)=bHI zoUCH*`Sx;vs` zPpS@9wL>DBnYNtN0#XtqD+Z<19QA2O#!3`2H>av3C%Z1K->_Y=GO9r|_0?TF(ug(M zsfVgD>2Z;^IabF9Wh7QDV{@_5e`@_9uF=vT!SfDZzgBP77YHt~taOO48%DIb^uUh$ z`infoEYMh5Eqxxb9)of#dL0(3HGTkLB(HK?r`|5C7LpMKO)@-WK;T8j%OIznZiwbB>UnP8=V#ywX^ z#w%pd#G^D3+yFp;7Y+X%**j9Ug~Lnk%jW3BS_}vJqIQ=_yHuY?brm}Bto2{Fs__T8 z>m`%(QzwTF&)35W3APj?m@{JQo40Vp&ghxSY@oCQu1}i%Y^G~yrc>?!%GwSUbZPtE z`JSM$UpOC{HJjhnCYC-NJ=cy1Hhb%;Dq^GT&FVg(_S`i`KL)?`?}%Bdy1Myqr4=Ft z)m|;AP?7ZW#NlI?Tw^Wh|f_hvJC4dygPAxw|6lgr!oKdcOn%DRBs|th9xAZWd^SbKBpPvt@oi4p4n^m-7BH#T&!dE0YfwmPv zJvr9_xZ&mt8a@SddBG5X^FI&lR@2vs84pvpH}Kr*=JYUg(t6T3t2Vv*z-nBnO6}NE zd7O;h6zmPVa$?uX!^?4*Sy;-w*#D+hP*|`1P)`;;LRIC&r<+@dCU=5$4=m8#=W_95 z9$r6TS8#2ZQPdPShq=FYud1yz-Ugeq!-aNd#NHAyp792bt!@mP??z0FA2Vkw_-1e$ zFc%5V;5y)fhG@XskZJ;5K~{qJfOyyR?QP)%$eys(X!`_~u7!y9`0aNY8C#Pqn;O9) zHV(3XM>dH7)_*;5Za{8E&zB~v(*;JqJMNKpY=6-}Hh^_{2F%S6Fae{5=^|BJ@5~Db z;0P59g7!1|nqyvOS9?e&k39|Qw|(EGD!0KUe^x5=>4YiXF%YJxZn}qQ55!Upy%(K@ z<~L{lgng+3LFW)>Wk^rl5&0K-bTpl5L`;>+E#Q^(V$QsaqM_u^Eyz6-cq3@0gW47Q zgMs~Vq_Bar7K}V#VNjuQ?ySq&@jlx>);I}-OG)PvYaoGb&st}{GXTOlRh~YW`8{XK zCi!O&8%jRv05ItdVe*_@YgZf(29C$6{J#S6FL59%7jaI(AhDDH&{8WCD?)$#0*U1U zif=ejaG`mbg5nn$D88S>9m1==H>n7{S z-m<4;{-#Kz1XZOyO--#9yrgMw?PQ#+F}XR?6Uq7(IU_p z*UZ@^jji`;M$ZZU{z^LEm{a1HU~O|wvH0%FS+3Y}66jWgl5kevkUa$Fb1ZQfV^SBg z)~s7uhAeXr{66iM`zERZg8MVJTQ8v1(eKDRRM39wpb=*f=Yuiz3j0JdaH)}79jJ^bPd-8#dQb7oZ4CAoR2{*B&Yq;uo2y@+8FZ| z&34nQ-JV*`uQN$pq=D`8L=KVU&RjtdF$wI!^$qlh=Qw+LyDFS2pxOY(1!G1jS^{~Dde#<9}X zTh;FEOqiNIfN*GhA@?=5i`;6IJ_CnLzdCeZm;2I%{XJa@R#BtYy#(Fi08_?wT%6?G zN8}q53FEtj9)%%X@jGF|;@92I{Rlhb&r_+EN)QjC6Sr;n9EP5^1?f3rtY%N+B&s8Q?}lkqvyO=}aXDxXS++z+i%7g{o)&7W4e~2kZ8xiz11ICtT@a)-*m*yU3z*{=Nj2(#97} ziWm#jI2HEQwIMUdP)B#a3U7HsY_^}U<6QPH`N6RFKJh_Az5^He)_fo?j;zw zh@gUt2+okp1-!bth#+0e5xU$yV6&)&Ps#-YBe`H;R`bHC_W$92fq$`YA~b*Ib^&%F zE>!r`?E){8MTpQlJRni6ajSa4eYlkuxm}>fdS;i%iRaJzu` zVoHGjGV8n4Qnw3;Kxs9QN|dA@uvYS-CyNe3N`qGm&={u?;>Uo9I@p-VH65YTZICi} zv%tkpyYUL^T;4+5EO0h%kkdNyRjEnVspJk^EHGRpP8A3?|BsqLp_1yMJD&4*Matnt zEF})9GZ#)x%iJsQC@{dU(;I~T8|sCze8 zyG1AOj?}ipd5hImMY>ma&++yK-CC@WV^ufTU+RxU-Cfa&ZQMofY!^9?!vuk08i8-X z!H3;e0@8Arm(o~<@<_EKL~0Rf_nJq|Lj*lNz@F4CYw!}rE4LjkRbiCiR@v?34oJWG zQpoHQk>Cdit{Gem*+P}w0L6@Rhf`1;E(NGG$tfH&5ybcVbQndp_T|1j6XbW!L{L z5{)Z8}}E{XmeqjG2}{hcnqYd6KY8b0_hg z==3`dGPXA}I?Psdn8MBJeAdt7-HbEn^~c8I9Jv$g4tHbS&8T1>TH}X8vj{AB8kt=EsIb%i8orF&A`kcVoopxh&F_8Wyi|68R+Du~Bt( zb?es2VHdX>%N@iYi|=tk^C42IYA$M>dxn28V4+DGYHJ2m)ms_?Q`QmPV9OA-g=r$63(u%WQjm72$7 ze0Ht*G8#Mw+($ej>mYBcEOevu~(tx*WziE6D$ESpc{vf+36xm6@}2>cse zIlMZgm2b_sODzAo8N^7&sr4?a^S{NB;0ipkzgCP?*q_f)!xi4F-BV2~rw=afrTkX> zMyc>4D#&IrLlOydA|~`vLP_yH{^J=CSHj2YcmO0l7;c>Yn&|Iv?+l z>vkfjt)1;H{nm_c#XZ`_yGx4JJg6=*iBF(6Z_Ec&+{x-f=vUE9TBt1{aBB9|UhPTc zPM6TqWAG(!HF}DT*5ct;lo+>qhujjDJ^YmQ4HGKH`Pw_5EA~aH8T?~>3-sDHt~}`s z_dt|(V$s{e^~YItTQS?&iArlGFPV!AwhUv_ve~YhALlLLS&Po88ISOe#h9QEBIf@3 z0M`O@!p0Spjmg(R%Tr-_{P2I?6 zE)41(~C3dM|P)!0etmm?S)~ig9%2R3(F^1wW{Mn8njlaS1+%r9>fqN3|z(K z{=R=hJz-d{-7od_&M_O+kYKyz)!77>&jwoxgh)c=(0e0?hOV{I^5MZtIXFTc6&riw zw|NGeM`r5;xl}diekGFpYEC%0xG&TkDjyzhJP^A%TYv_tXdreCUTrna1=(!s==Nr+ z^h=ehU<3NY`Pq-uxm4;*qRzO%I!=WnRFyiHW~T*j^4D-fM1-5JtoF9gen2=YQAFTa zubuxI(M-*&d8bgITl>y8c*QKbdo?S@{T7|}%k0Xa8??rY_y{z)TH`}VQ_NRUu;I%E zVp=Kp=A}IiOUk{+BDK$8)R8}k=I+oFVM_(da~(Hk<03&1#-SPGwZ`}5{nBS*Mar2J zqflxGImm35Zg+7SuwrZ^8P1VQ5DC}WlAC^j!+_MUD8k4TNHQ`+y9F{dCsvzAGGm;e z#u(=gkngQl`$%2Y{jbGtVq8b=v+bdS(qrQr?q5(4J3Z7qIotBu@Pg*h^x^41gumG~ zLO#bm9qxj383g0>q;AW-ZYj=ae5BQ1(P~VS74Lb3SK7isHX69o(!N#5GDx#Z2Ju+! z;43#hTyUX=A2Roa%ie9ce=#0PyTPnjw;JVq8-LAScSGDubE!Wwcy+pv){LWh4~_-8 z`co)iZ`Pi4&#L^pYxy-?9`v^Mj?mr6@zd()%APv0vU4At(j zlsp@LJ8IrJH(2)iZVPwX8nZ(rQU08rcoxcEdcl^v<(t9}dPH=#eLW;#(FgD=6>zsf zIDvL^Q4b2+%x~KEl^H~G;ZtYW{dQt?xt{t@$~5iSD2p>zgd_f`|0_W*Rs?y=AVG4t z%HK8XhbGS_vo08TCdL7=8yzxNC@&@Q3Us*`VdbO{=6DE`KPprlAI|5z)PK>f(B?mR zX0er_&Akq7f^qc0Ex8%ueBeGsk|S;3$M?#c*7PF^K%kCr0}ai)_p?MAP@}7>n!lI7 zdO=|4+Av(oSqDO@Yr`)ONmgZNw0U0nrRk_paq&R?IB`{@)0Z$+dgo@@3t)h5>$|r= zTY^A(e{mIo3DVQ4>B4N@X33L)Qjh{&FV?;#!cF?jY)`@;2I#sF-*HgtpwJ<0CQ!(r zCh$qj8$mw%=D#z&$4+AIcnuGmuiL)VD#)|n6Q5xHmBSKeC$hTKE1cSu3SyTv`tOYA znQx^32l{xHPpNas#I7*jdXyA<%&Nhv(|=2ObuHwAfkV6-uFu@zi&%j9K{m?4T@p<{ zDBIin-1uqOvNv8yYZb2&czwn|v#CwMQt_(njX&otF!Qc=WpCs_0}^;IYWB$`tI_1l z6=V|_hAi+lcTDE>u^^*V8{WZjl>Hmc~ zud4Qj{MbT9;iS(A8eio8K7#Ij)>>6V0jP_R@5p5JLX8(S|R^)bin<3&Qf2Q-fdM;3B zw|UX(z7!dZ8;RvQ^HOdplAFr5@OL~{6k5CSHg&GO+N5IX1s-JNK|#jR1+l7Cqko|# z8Q)Yv(Y7l+#lF(J3MahWW>{jb_GDYyt8Ln9O~y)rxE9YF?oQ|0EL|rSp781D7ulSM zx@KVJE7fbc&mV907pvDkYj3xjm=@zQECfxjKKNb+r~yl|V>ud-TmRo;y1(qibYB=; zJ0zrgB;B%g(R2J1iRd2X*q#4;ne{PijDW7)|A%mHWz)&}hbyr!`G?YS>T@pKEgOmH z>1g3m!MSi#7aUD2{VJY&xk!ymv8psU0p0NDB{<#kSTGRF9VNAp|L0lZA7gh`7jv*A0o~-iX{SMpf8n=K!@o0r=sbuuu`oJEe|29ViRx#awqL9&lx8u_+ z@!Yj4o;zRoQGeXIi`3{}r8TwFP|I1APS3TwFd@mG$H9KYK0?Iyc76Aev>!wW0@k!E ze5MQRt`L7kCm+3^Qisd7v+L=p`)DT{)O}zesC$VM)QyI6@4~!mh@_fZ9!y?yn2`8u z(pP5#xewf19UhTJHg;kbtv{WcK^UYUo;1B%{6j;x6$VrC2PFkTPUyBduQZwo+P32P zLLY@I24c6*S5qskaR29)fq?C?PQZ4t${P}}t2&wPgk`pVIM41Y*2O-h)C~|XSs)#>ramEx4ajCWvW0r@? zme6R~dlbpWX){LLlK$+s`iXI78+uHIHOn%e%O{D`4wd??3y`I#f>bf<52 z4x;$**dbn0)ln)#D3V@-my3;s=YC4t$DD5SPBmf>P&mty~Xa~TEJa`D33TGJJrR1s&Z z_V1c?L*r~ka1bY=zdj^L{aLA>bxoYD2pEG>_M&#^BND6RcWLZwewT@v;P}e;ql%TM z9|<;8E{hkiHA=cL-3(_aPJfGEzq&>$xK{Rz1KNy>yCkG(g6kFvTN|L83hX(Ot6G8mRfCXYg@Ff(rQ~?S8!`sgy0Ie;ZjYlZJ!vmu~op0{J-bk z=b21Gu=ag_{q^(y{vEhE=ehemcR%;sa~WJG3uH(gFOV^Gq`*~lOM&Q4@c?B8DwJ03 z^E~v7o{p^5r?NCU4B22Yb6441;okU+RW3_dY|64Xj)v8u*Gzi8M>!<(SESc-@M_mV z+jm)kQTEeDaavkCyd7 zcv*PIk9h4jBY0cePdGc}9;KX&9d}2j_*L`%%+uBrKZV?~qEEJdrX%T#f3_~|^BKsH zQV}5)#C$R<7*~#pKO~Jr#z4;bWzeO`-$S@|jy#?gxeMg?IOlfW1F~Q5t1EH4zcAZ{>yl zn!Do*d3B%=tMID>F(0rYOw}909JXxPlvXx-9~{;XHOO9%?u>)z2w<-_*!s!+;Z5=V zpd@TId-oBN?HBrAjja{z@;FKM*v@W`?Tb++FFIgPyuTW3Z5a(G+DOFj2*%c!I6gm&sPu)rv`%3$%p8J;WdZ_xb#PsWZ%U97u#ii?3=^c9SA|t1)zbi1= zR^vw6lx8C(oErmNGnh9hBVC$heh%Td?&{Hy~(g(7P z8mdwFWBuQZSWDA|mt;46eN?WafeJ?JQQEO6R*2L+!KbW-h*{wX@CWN9fnspe^& zRJUt)wh5y_vN-|E*1B6{0Z`#tf0^t{v<|1qFnJhi-a&`c;TV{342w&{bAMY3u03^G z&2aV@={iOUoKQQM{YG|E)r&unHz=}gWmfIq5lvQ%P%<)Qi&VsjV%Z9_E}1aa-q{^( zyPU=vsV54_PIQc(K$q15N<-_hby=n8*ksv%(@YT z`^ywm-NQ`d>}6~PRc0SUpRayGHsLu<<+89@y+-s?!Nsf?yHxfyLf)^pU+HXY-dTN- z_MM&ZXLzQO3aXwRX;akGP)Cbpp3RC-QWb}isyJ5S70^JnZKBf%Da}qtN9cQ;J*{Gi z;B0#SJ({Zeil(Z}W1e|DJ`xyP-J7DSZkr#J9`vH9iree9rm7dTG9Z6gRh6g=)2gbn z*Z-OJ&t6a_;_QqG=n~+Ag9_ACWp9|!_VH(7Jyqx0daAxp9cCUiYN|Z*j?(-6J+xFk z{vuI0TB^$MuD3vd;ma1=P zPcKAz(&N%`TB^30#)O8d_E<9(%Ba}(?x&0d-L+LMZTr+%Mrx~CYP415X>C<`+q|?a zsZPBQ>P=gf-pssg&1R#+u+gQh3iVduUC<&p#-!bgwkkVx4539>@kFYs3cIPQdI(tp zVVCt#RaL0h(pDWilrB|O!u4I%K2ZY>OJy2u9}~`~PTr`ik{!^m@6}T`Jt=Gb!Bv-Q zbyb(>ZPj+6gPqyMB%qrnc`!<-Bmi;BZphQHfB`{vL`T=La-#J}PMN@&uEm?JwQ4$^ zB6MA~?~pnBOI29)Cj@iQdkJlEV4@AmC`Rfhv%febwtc_=!O)Q0_9qZgVRc9>aPo+j zs$NxCJ%o=Fs<8S2ju9%XHp*u?bTCS(zA2w<%I!}Xow}>Ax*VG(pV#=F&xd5%=$({_ zQj0gOGW#E+!b)=~tY&sM(5&q_hI6BBimj{O+UNp1>Z=g(^E4t|tU|{)Yw>F#jqcj3 z{B5j=S-a>hj=$|`omEkX)vNX@z1v|SC=@i>tCqCM5lnc~gH|kO(^Dtj{u%96i;2|T zevw4oK9|3)_AIHFI9M{Gy=tnXx~f75<7{}|HYGEQieza@v>`1RCd))kj4stxM}=w# zsrF&j78jg#ycVmS{w^(6i`GhKz5PU5tgP>F=3=i{&%a4(v@<*Xu3alFDHqJ@ygTo2yml~HLyoN zi`qP4NBeo%JU|@U`-m$U#u|4IzHmkPN+?rb4zm^~w@>OpvOs|-EHhf}gz zVR>kJ5Cm<`uy(rWkvHKW?JZ`&@x_imzSujX5WtEk_LEMrO~l0BmQCN{9-HT3WUA!l zn1jKO{D^#Ur>(O^;^oMCeRPs=HaFl82l+K3mKgzOurL9Q@horcg_$yhIQ#Isxp zle>zYDHmUguVSBeTdmXpNL@+6XqXZI93pA@MAEIZ{^duL_x(md=SX3igA4Y&y^N2zwh!*J33~ ziMY+t82jA)*pPFs297w$X+3=NF@XgV!EG{zp;Er7+7+1OFaAK&LS)UKe@4g=C!ye$ z!oqw>ri>52ujQgIlABaW$@`mz&yl!-4-m1|Pf3(_ApVipIPMD4;qjrpv87L$JEw*+ zS-s1~cHI}uYoxZU{f#258cG^O&aHVSMmKodVKQvjKT>+(Ge}`ibf%m`1);yqTqMj} zK4T;YveJBJqy~>T$OjYlV&yNkq?F}P3yC_Ul$<%DCWfiD#Tqg~8WFd$xb5@DuL(~1 z^#Sd1XQ4J9fyanAOAL(WDuY|}V&^7XKfI>16UEp^Sn5%7Bmo-dBqN|nn~+=h(%<|c z*SZY-AjX9HRjDz-aiJ{lEHCQC11Ymc3FtR#w1Bu-D(eRb_FI49+~XM{lkO)pkT}pC zKu_mB&?WjnQ};|G!{3cITyWwR?46IxSc$y9Tq;6>i7C$?+O%2POX#T?Gq{h~bbYgY z@!o}8@_Wzu=H=!X+@nR9SoYa6S>}a&Zdd_mALaw;%-CR3USqBsb!wk$Fd?$c(z*ZgJO4CKn1LyvCd zE9lu1~A_lJqhsi*}FsNpRhl#m^Aa2vrXxGMQ6#e}ra*+570)b|b_`z@SL`P^QwqFoi zU8V{Y$Qa=!bX~*{L2XiF&sz6NP%}i-b`23%jn;G215qjF~p89@W=ICI5n5pk)Jv7>LOEX)$ zki~kaGY5aXoV_u6L!7^Jujiqu;_{sJQm&pI2KMxTYgWVIz%X_Xzs{;V<_+}WZ{Oe@ z5=q}Z=ONMoPvq&Thar=v;g95^E|c@ay3D>o9!uNR{-L&)wV~V$;dP&xVag&`kP$ z_QWlv43cHmF747h0`quh**()6IB#a(z#Is2mgfof3VxwZC#B$#o{eO9moB^nwCT{E zfD;7SC3czy2<%-V)nU>>kWZ)6HV8X?$%RW%WATY@# zgvUbDp9A9=t(>>9Trv0TWoUb4PwYncChS);7D;;>F$&-Q##yfk4;6t?D2uLk7}N4b zlwa?i;HJY4bxxTcm#uYifH@l`u>OtoXMR|_)L+cGu^*K~wHKil|3iP~ff}ayr>t>L z;@?a;8F@{-AsdcYPbc=-)e2(G)&*^xHIl6OsPg9Q#t|Oy_Gr4SP=W3y8(H1xPrNqB z;(e%vdTC&i^)%?76gtFI%$cz)EA^y&IE=j~lWGP6iUQO92R_p)p={nyL30CEX?oJ_ zOzB6o%#2jzMbg19KmyU89ep|m9bAI3G}UXPityU#g$26XC&=a9pVo@7%13(s{2BIK zHE73y+4NSv%qT}uD;yClb`E6}I!o@z$lN8>?B#CTw*rK1npFqrU9X6ql$lUjzea|; z+=N^56~mcZc>YlA-M5e)V@kbr|-c!U+6=&ZF_U9RBW=FR=671 z9?IIVc8R}nZAVVSvjKPG+M~XQliTC68%vL7Z)9x9KV&^JR~n{g{i(3}waCT#j$rbU zJt`}XA!J6*p+Iy_{1>6;jQ$MR*s9q#W*({j_BWW z*U8zFY*btD&oOWvAo3VEJJiuWH0$slcfd`OiX`9ni2!9*J8~Hvq5MLgL2C9rP8IR? zRdQgW{23#EhRPpL{U=$$hMdff&?}x>c5?n7I)HZC&`a%coQ<_dgF19Xj+6|+v?ogovVvn4w9_vgQoKGHGtTB|qdh>e}B%|#|&{rSa#^c6@@d6V~_LoKT zJllS5)g7{4BMwU6+L`hWR;=}YX?+W;y()>)wBPQ_d@|U_SND8YdtXuU5CiJ=hZePl z60AXWgwz>+jXk8vuq~#}Tk|>bM5XB7Fy_6}V&bM*zSpSBc{hsx* z49{tR#q|rCny=yGKrob$gF=j_I<4^t>NMuGNUaXF`jEkO8R9#TPewX9fozitWN52u zTJ)mH!}7+pFIql!oDgKl^7^$eo)k>xVnz%8zndlJDxHDd#4gjc^;9d24J__AL3I{J zlZ8j5M{ienU;npYQYh!pn4Q6xgb&-J5;~~#oiz73vt*SSIF;=bU^HJ*x;tb6M)4J+ z^j0fI1xI9W$XU`pWV^g+XSbMmZs06wkCEZV^kjs+XhS|8pUV!dZEjrK;#vPwu|PtP zvNn&|L5wQP(;#Akg4PA9IrdpEOi6vWp+=C*KV6mVtN%Ras)_uKY_0zn>GhUb$C#XgCs79%uo<^bz9l^Fg+6P0 zkzCA@`~*kpv>BDG^tbF3Qb<9_rMF{F)&>~Y_F0rZu!@pzK|h&4)t8 znnHOR{%$OFt#?c}1q+_jCK|6GhUD7!xD+jvkXyW)u-rh5ZONIi+sZsuw;49LvgnF# z&B=W4y4Tv#WxlrAZu7+n*&9naF_1Ryt9$1`PHihPR$HW4OMwAJ^|yYtp<*SF4w>HypQ?1Xw6K*2b{e%eZ(gGp%9@*K#HV|)tS9v38 z6?#p5M|NCC1S!lD|lnbb=G&6jm9m2FO z|1J4Hi0IFlx*AaeiTaCu510{lIxBQ*GfpBn4s+^x>$~C)sY&~WX9J%sWt|(I z`O(AQXphbd{hr&M8Dp=T$(1-6>m=aUbS#|#9c6xGlv&-QJmbrwr)avT&b;tHG?u8DGWYjHP3}*Pi2Vsu(+#OQ@>`a~W0csd14u&hrowoz1X4+WRq3 zleJf@EnEf(wTLd-$C35yd@_^JYxa5`-qW7tFPd>+=# z$Mg-{RW#$c<&Ek7`Z(CQdZ+XX*|W}=DJ7@*i@0HSi4;;R=HpEsvsrT9vJUT;e)~OS zni0MsSORjdIUxE55;=Z8*e=0IM63T0*6Q|e>AhI}K9_$+QVFX&dLe6Bn|IQs>wJ-| zBotP(xeKGU&>Rd56gi-N*)SN!(YXULh!u=7d%Hr}#+K>PArA>v$u1f?S&g^KiAn5o zIWf7cHD^Zgpx_wUlK1gE1OcM6GfI!@3lkmoA%Z+hlDhBNvOp%jXDb@>}V@1N_D7B(R?s zdU<|rg)86f-V+^Gk0$Gi}*&?0`6a2LTD zJI}x4-DL0?;FE296!;Kh9p7*`xE-d7i_XR0WBTtG`tRrZ?`Qh&r~2yHO~#8%uPK1HsL%_q6bS${OZwaRKaA&}0M`Jw0AF+etMWz42&;qb&| zAE{LkPg^VWqTnk`!Tm>ITv2co4(6SioSWHlHIH(eLdW~Vgwkby^HIC(!a$UHo&iwp zjdsdkEMuk|bp-l3<=>SI=izl3bSfir6Fy=^e=-CRHJ*W)p`2=RM8;v@a2N}ZiNTm! zOOUeYt+begR$1P3&}{+ye^Atu?V5*E8p#(`m9y< zb;&1akruWdkk}f=%1SC5Rzx#UJ7+W8 zWRbxP9OV!KG~Exr1w7AiJJa~w%%`X*dl`4H)&cJVs0qWhQ%12|Oi_Q6urY=k4K4ZstiwB^m>oh`)LT*Z%PWU>!~~LzRg8X%B}UY>>}ZP(USyDH zc-Od#!V+6$3(r@!#>sM<8`HbAz82EZ35W)lzl$XbT;%5&$#BjO)Y0eSWpzDUBFqad zjF(lI*Wc)C%@Z{)q3n3>IWL6kA$nbW9atU>zDQyt+rGgl92wsx&LZWpw3-LE5ux&= z#>9J4v*WY;>vq)fO*UXrwuz5zS$yY(5>0w}o?U%0GXLkrCre_feC8&LU8>l5#V(C( zWr=;O*jr+6GKK;OY&*pEXz*9L>nuqD=@S8-ddZ~GB(t5$Jih$UU{h{1igCJEkiT=E zQ%Aaj{Pk^75tXDX2)meYB{>yT&{aY8ZEm5dCY&o6uAn$mK^*dgllY4DlO2ClDA7T} zQbDQIMY2>7gd1d%@gdCEKlqZa9v1iA%d6{$+4E{sKh%X(OSqa${p^USpFBG~q3=br=F%riMN739XU|CiOzBh-&#iTr zmeq48*KJ+%HR=5qBwODwNUBw45U+K)LDH;?4U%rtyF`QSssIASbYpqZGCZxPJEU1kw!v7Gs`mg2EpGj_$I;k8(hX0Yq!BS3%7<|9r)doK#c!|MV1z%!tOYl5{cL<(k@S}oH zGq`Yrtu%wX1s`s3{Qyj|!BfRP#^7GTk1i1+m?vf4Gq`@yrPbgW;^#$!%fj1gF}U1; zwH`CLJP2cLHF&k)KR5U)!EZBoo!~bbe1qV12Hzxjz~HwDUS{wz!Iv6*i{J$Y-zs>v z!M6#XVen?bPd9jr;9i687krSxHw*4I_#weRU#!dCDtL#%Ey3S0c!%JJ41QGbXABO< zR9VdimuI`J2MnGp_!fhw3Vyr6y@GEtc$(l122U4!mBBLvuP`{QSY;I&+%Nb-gBJ+y zH~134XBxav@N|Qh2|m`~)q#8tO_fHx-Y=jmH!d)QimkV-sy`(y(zG zn-3RBu`l2S!K7n1=xn}aY%;L<$k;q-j?C1ieG>kSq|d7-Cd4K!?{Yxc%Leb3$*yqKHjM77v|WJerfgMZ%CwH-dc zX;9zg>)!74EMNEOQP0&+vj|3sBTZyy@OQb7INRsE=!5?H4hn|mx~V&J*Y67KZTI+x zvEe(^xeLytta8{ek7tuS#@;XwlMS}Dio_aWRp#ELByibxJkiatelP`ak)V~`YSWy3NOkh&|yL|$KJD&j$KjJV1E{YqKx(^^OzN!8*cc6d$ zX9M8|1H0p*>bEuoQ~p zj8IY|M?0Yd@EE+I*mdC1Etv<_p2nk!T2u24n+brBN{gG97m>yHhLV=xsr?1(RnC8M z8)L?jvp8~g5`x>mbK^PlEsjIKCuxPAM@MjbY=~<}FJ->P!&PLtFIo1iPo)XvHR}9k zzU9$u$?Qg*%eF6M19?>Mfc>7?`~A`TQ2|)fU;JD|-i1}v96U+$jG8WH8hyDYSKOvcxr9gL-+`{B zrr}5Rk^b`&iM26S6l0;`t20F|H~HbfH}T?H%6-PMSUbKcFR z81cflrNl=)>t7PGG$sAaFZ9dT^pfu7Y51;mt)`S~aL}c>LozH5*XTaSUGu-5u6_8m z4>)+S*Ai)G$|~_FchR3W?#W^I<=TCTohiwVzZDWsV{9s(&}|)x^$5}rqz?!>{o^Dwa$C!grV3o9vo=$Lgp%IBNkB(u z%IP|(R#C|{QxZC>^JM|BSK;yb^eb?3@h3yG`C#LJOf0_67x5Bzm^%VUW1|%yg#(^Y z(mIJV^ZCFu-pvw$G5nm0T(4m~j>JQm?O|YN%7eBC_R#YB7=A)YBI4Yc@*~?NnQI5I znNW15z0gjY9ahiv48usxvYph53A*~8(9C(zhxUuAG_s-p91ME#!0Q$JSe%fv0pf`Iy`k-vUY&tiPqL?X zvbdHFYS-%QRTNw0a;_E}ofZE#A@+KUZ!$4dp*1|c4o(ssj&>wkjNm~aX$iNMcV14@ZI|{H zteO#9yn&@U{r+j|$KTficN6^epS51~xY&fSu_`(9-m4Oc$sEe1%lMrkgUjW+tc!5e zgK{8^X`#jX1dbAKLcU~WI1ZN@hgR(%0-TSU^Zzg(+AFW7aED6TPGE$v?$2xWANhN3 zW^=8_`jB8w;_b6g-wYRiU%+k67$s$3wB$Xs=d4%s)FPu#V6f=L>+hd{RBmFN6nK~Q zA^ONfNwq$`Yr+CA|pKr0h>E5yX|AZ((`Y_fSPl*yW&O<`6hpr$o84=fePl5_C zaAEblI|_9p=={%tjKW&}Qy)B05hJb3$n&TS>r9<>y=?g_8$~(U+kv0F5JIzmL=C|Y zZ)J4f@p-JT{x2itfeVp|Ey%yJbBS+bz>^`fePLGA;jI0~kn)bwvfi#>U*yiT&fXvT z4rhDNs-1*Z?WeU??I8oHfTyh&-;zr7G(5#-l0>GH$oZj|R=mf_>Gl0sTV>q8Vl3wn zdnv2JW@#f$u?hH`amgUb2{IfW&n>$;Q@%~zNn~pY1t+^N;^&?Q*%BichZ7V)-sAVM z`bpKsGH=pT&i!vuH0x=%)GL8)31qNbEr*FT7eaVPc5%> zpSU6JKHQejp@j%9+xp|%wukSC2Lw+t^xt&FptzLtz_Eqqf~G!ooqABDH)4e{92UxX zMrX>|0LWzQKOtB?ny+XZb^=4+M+5=f4>c;9Ej z7tu5vdBuH+=f+sr}mV#cafb!(7!3=m#mFD z_fnX*eH*epc{IzneS5Rx3ZQ|aZ|1dqqFdH!WBEMP_8uSFwjBftUrA^ogl_n>2W*^$!WUD&UoL(n6bH?yJyA+6E+Oy7Cl-d z*t+q5LmxrcebPxks(H>oiW7E!(|QSy3YqK)OrF`)cT>_IS*7|zi958qAz7j8nwEO^ z`gOEPNKGP&=L73boh(8E8x%Eb4b zzCsCqKgN_WpON=OB|MFS^ekbfl(0Vzx?I)bW1CPw`Y4B_T@^LCdx;WhZE~8UMWaMK z%03I?P-P1wuh|pXqop@jPoOUXq#rLL1;pD$P4W*WphWe+QQnqt>cn*J%P0?e1f6Rp^+8hqunvz;&Sx6HQKa3hu^Pxm{_Jlp?Umh)V2_!_b2+z(u zcHOpiR_segNsE@x6z*V}0y7Ty&>(SrGz8JD28qn_-zOuCpD~#2Ct1kRYrW2tIXVZ7^q;c=qU}w6z5VCR3nEV6wuJZbuMb_Fh^uaF_0jc?m?bbGyY)f%N3*m#X-rb81yl(n$b5OyH4h^jj z?;S>*F8#NTsyxwu`zS6w^xr;oqkHS{Nd33A(yL}}@yzu+)X;Z7uD%@>8n5(9>nI8; zWWMo*T3Et*8j8u8h>G9nHgK8^|8CpAX~WxX*gzIUq%yV^w8t3upxNUace9#R_-3US>Dy7DPR zH-)(8{clrsI!>Z{|SY-y7{zE zl2~;tT?%o}JK8P^aRFh4xZp84q4Rh&3#GaLe^7{f&ql_}6Dq_-9x>@zw!oTrkqU9s zhtdxIM+$LoB3j;6PL+6iQ;54@oX!^J)DhX;)xaF))?PH z#uF>V{p6=%Li-~X;(l_LPRdb;YgD_+(m1RU_xThA%r=hJ8gZwykYvIM#QW-x#-WCr zrP-G&$h~>GS!8~hg4|gsU@Z$w;;*A1cN5oL-cM+6tUJ4cI~AQfkN}=GnIX}UEB2_!we3-nJ4x(IQ1C9W+|zKfKvd)o z7Kn=6egaXE+eaX(9OYh;s5dHBKPasgRLU>A}1PDexrbo}5QDqzeS^fby<-qp+v|cr^tiSI#wx0<1w^RUtBPDx8gX9O_ES7s zPhJ*YIbNG>tH}N4;mG?&EYL;JRWuG~upaoiA1cE%;+@V$9agpqUSN2^Q-L6iU zbJBmXKT0Ncwkei{jHg-6x4{Sz-MCj}&dMaM+RARaakH`NZGR*eT+%3S#Qtc2eh0L$EcL`h|cCwTyo7meir45qW_ypeM~7y_JZ z!o4-OO5no44Mw7whm8*g&6N^i6-SLi^G4f7iHoo3`o5hAKhi0$yDG)Hg>ww&z#wln z-Dp=k3PBe!lIOQtcTY99OMLa;9Hcz!g{{VA#ti*NEh@III$w@_28a+m&$Pf=7e4g2 zzD+Ychgi++4r?lC-P)rnq~tnE_!fw4nd>A+^}7o%mwhrZr4v)|RLez(rprgOeS6d= zO?WMLNMwkL2;H`bZ@5+L_4@3MX8XmI5|qfxsj}$AfKM?%H|l})Yttw(<>zSf^}rqQ^MA}coYYVK(Q7>GhiUuc z${xCjvd`w&MIU}pfKRhb;XMsMXINmy2i-}^sUw=|1pn$$98FRi2rB9+R;a;6~fxl?~TJ;rMl$xRda5T${3Oy zd3HcHr@kNhl%wU)@8x_Z#hQLecs%;xTy`Fx5_w)|6e>%MdX`6KVIhaWG3nCOEP4Zc zd-0UnYP0|^pHUX&4^3ZECd?_G@4IEMKXdwgzJgU;s0@9;twqtX(*89#du}e1&FB~W zxU)H|w`<`#p%2|cPDbPn;=b1QYjjo68JYvb{1g7l*k-L~rzh%nWP=ro;f$?0Xia_J z-#8hPuJSide|3d)9@zT7Aa5Lph|XG?eXhijZ9Vz`F*e5TE`nKf_5H%GU%lG8>pso5 zueQ!u;?O`358-y-b@osD&mp!Lj`!Y@q{lS*-PTEUI?{PM<>mmKq%`PIU@{W)YAs0C z$Jc33XWO2BVmwWd&(H_br*8Cz`s7b|&mTILd*BOsAgwyT7?G^zK+Y3F`h3yTwO=aW zy#Hbv=Bh?;sNA5NJ!4v#r{NBKfF^>lzq zb$pN|ZU^7_g)Bk$*;kFFs=e0BnN0oS?Gody?T2{karT%c2aoy=41CE?U`<+E@hn+O zlbdqBhBeV6f+J~4DPrg4v@DAOSKpi)vqz59DP*iZW$o<_9b-s=3?DLb$R**>0pE6R zH?fFs=9V4@q$r^4b<9J@lzrO!?$l0sSMxj<5-Zb>m|=n?NT2|_D0xvAH7I0QtdNQO zJ(_tKvOPELAeGLPRQL_P-^s+nJ=g@#ux^GYXpUE{ZwY%4mtMy` zdD-kT#=b{X9jwOZtT&0DvoK!6%*}kuA9^XrlfM`1d(0Ud7u{|%Ik|RN`|DOdG1q6r z1{16?I=LhQ`+2%b^zuJvamYnhSH{cONPldZdayI)YQEYRt-cIG5jmdDW*H}iH2NvA zXgf!$iFMgbydF8^ABJ4ZTij0d*P{@5ob|{8DVHQnpw}3AsEltK@!{1nR%n)CuKi>d2T@PY-k9ymfU~yL<&J9ht@~pg zsbzbf*zY^=DK|Z`I8|Q)#5N!|KM<`AqzObvgjXQiA^fxJ@?7pZ4#J-1X1&T-$G6IG zwWs&6zh2u%wWs3C<-V>x*>NWm*ksh9a3>h2b<*&_(vjDOHIGxx3MDOMLMqg4%m2u< zG{pMJd}m0u7SG_YTUf2_@uAq!aCI78P`uu`56<9JF*em1t$8(4-nZr^QMU)K7yX6e z$OG3;c^em`w#}qp_VU1WdywMw^1$`3MHICA1J`3eavIco(vn!eGQfG;himmbayZOd zF+21mmL+5T*2{mEFA5+U{qO65&=u9G-(S%t(!U9u$k=_u#4Agc&UD^ zGa+fiXkX27H zll;60td$0~ShuqcVcI}V-QM<8lXBOjVC{hjqV&=bm-9K2MXRc$TmK#(B`Ad84-00! zBIKOUPopJ*M<^S2;j|FIWpNa_G4`${Qu5t?qnCl{`BrVg&HY3nNT5$=N+?!)N!!&q z&I0Wm_pbgc>~fOi&LgRM{h@bR*%w$JOb}s2b~jwpjC9GeUhL@tStLxM^@#0~9vNmk z!=bWPtm!2>Ct{ZaWhL_dg=sbxtI`?UY(s{cWdi36hm`YjV#_nu1YR2SRS^ z!Fzhk4da8dp7>^OPI}yycYu#0iI%6cHuUPGL#>Q(>QOw_6w1nva1Rr@{_#58*rSS#BR!2%5`H^JUW8LYM5t6CBi-t*er=)B!pCRzmQ8EXmAzy>l%Hj7up{f%TBR9RMK}mW|MUBQmIAG3NCQ{u z0~@L-=DVK_(`hN3LD;F!`p258yoJnVXF-f+t5AL#Gh)z(``7@hIuwzYQrmR zc)bmOXu~vFnD85H!#*~A?<`~gk?l`SGvA3e9BadwHoVY=SJ-fa4R5#MRvSKL!#8dC zfenw@aKLnv&M7v$(1wLJth8Z+4R5yLW*gpX!-s6R(}pkF@NFA**zi*u#-C}@_1f@s z8=hms`8NEz4XbUq!G@b`xY>sH+VBY*9d$J8PZ0NV)*KN4UhBw&odp7*J z4Ii-K9vi-9!)bOs>dNKMGj=^bWWz&Fy*eIF05^{lrEW?MDl)L}pn=caZD7w}?$3;U z-6_4hNBVaqeXvZvWhs-7X+5lf9K$B+5tt0KOO70fdIn~UFN*aWqGWIRR0(`9SQqm;?N zf}WCJu0`s6O4%h}PJRrmb5 z_^R#UZ!!5O(IxNhvJl^;5x(=Gab-l<1-N(rmV7wrDq5MOr<93bz9l{>hr}cKmhh~6 z{AaIRd3J5ML6z`3-J8$PE68eo_##~X9U$&QBAml&o8Rf zpQNiuOA)`st%y_N!&DM}wIVKwN6jr=rU;`J6a|7cB{=Y#TT^ah(4{O`Qycz*UZo|K zr4bejgXSy0s#5z}5VT=YK;n_`5=P-q;YZ;vNhnuTbWCiYICtOpgv6wNp5*=m1`bLY zJS27KNyCPZIC-RZ)aWr|$DJ}h?bOpIoIY{Vz5Z6Eh{c5UB05M{E90pR#sM3f1{>0 z5WMQ@RjaT0=9;zFUZ>_%)#R)y4;0i?6_-lwuB0s$Q};Erf>Je!mQ1^kQj$ap5>jf{=b z56da_3cf0J|1H;JTV!0~UQU|jxL5G^8rz@ro_O86O#I@n1ovX?Ek%|D6Jgeb?QlKSvM87ZZSbtSekQhK$|E6Kmfdw^aorI%W)CB_Qvr%Ely zPU4d~bxJ1VQx}~kYC5eXZ5dN#%<-x;W`ttCYSgKGEhoN8zNO5PC$W*1AoP?H9Z#uB zokwXwW)6_@Nehb%nXU6Aqp9R;lCE88PfmSL3DqbeZN0_i)ooDPv6H7R z`c6@2h2wMb^VRC}YSQXG#op`G&|wOrhLiuVo}Tn9>9hZx^rnZ?tEP>bHgFYj)extw zIx3*r@jc1un_U!h@;@yc-&fE7<>Xw}N~=gWKpz$gIbYHuom%Wl&8hD*)QoU?z14RW zwJP;xMndV|ReH3LQL~gWQbw&(9fQ-39B9gOMvwL+xsn)Vd@y5MC@_T%IE1|lKfkF|&gSBdxJJjbsld zzrtj*-;$G6{j?eC%Xx7YqY$^PD&X#8`vLjSVtZ@HWyzm5ds&J_Ut+hTu@w7*;9jl0+WuC~8N z+23_;()`k9?#x3GPbjc&-~JeK}L)U`k?&MDuWdjps?}#aHhxMYIGmf zCn`B6CnqOXe$&&5OFVir3YNsV)miE3iwoeNd%e1exeLn*`6;!kdKEu6K6rV-?FP8{ zC!hcMK>_b^|I!!-&A;Q_j<@ksGhgz_+~wSSQ@T(7$RMZxp=D*v4D z-v6|L>tB@XtNnArAK#+?S(|^<10RkcF}imB>egLf-?09MZ*6GY7`n0Prf+Zh&duMw z<<{?g|F$3e@JF}*_$NQze8-(X`}r^Kx_iqne|68jzy8f{xBl0C_doF9Ll1A;{>Y<` zJ^sY+ns@Bnwfo6Edt3HB_4G5(KKK0o0|#Gt@uinvIrQplufOs8H{WXg!`pv+=TCqB zi`DjS`+M(y@YjwH|MvHfK0bWp=qI0k_BpC+{>KcO6Ek4G5`*U7UH*S}`u}74|04$3 ziQP4W?B8AfSk8mxfZq9y;9F$LoF6iZ-M*Xnj$BLJ)Z?4mzunw7_4wuvcsKW(dwhSl z$G1FL8JV6uYZ>`1(kHT}ZpO$-{CTAguW@mCWl7c53j#%fa`>UxFRCrAnYZkU(&9jF z*`q0Mc+_&!}WE8Vq;m+tzW+$!l$R#71V7|Zk0AZqhN6z z>opd21qB-j>P@TLP)8`mvaYPG%X6^@^t?zN?XK!meeS#+g*)&@!_eR(BCFW1F#!gsk>1p~c#u=CgD4_bbS zzeUuG!zXcg%f-};a3_RUA-hr8K?uJ?ILLQ+pNIj<;)4aPup!stnXrRd~ya zDoZL#YrH+n*;RilN&{41dB9s-RZ{A$TJEiOc=Zy~B+^}laek9&Kegm&GVMTeF&Q`6 z)jPkORn>Gb(=trW6Yt8E6X0`$Usb$wOqb8}>qxrm+(r5?Db-CO(vLS-D}-6JaPCBN zVjSsTr#yblcyEzi3TZ`=p-JI*|D(o3+KP&*t0iIy-J>}eq8%5mdyV!;rI&PyYE}fL z!fU;0rB^Xhl`r>}uB;BMKJ_1`w~VG{4`M}Rw77`Y;524wu-=uWE351y!O?b49IZ!G z>4#o*ydC_r1=$O3T{GeF-?yBX^Mk`lj~;vLYw0eEI_K=AGC$QWy_iP0dMW2+GEvno ztu0?!T~T_uGY&5;DX$GI4V*b`Qgw+Lhz*%e_*dfYKhUiPmL#fy(-PFc`JVkr%?Z_S z%rWu;cY2k25|bqY{rsNtD)lDD`R;#Gj5=w`;OdmZLFp1k;@dY$slQ{sW`}VNjaNeh zNopu*3|*L@hEC(VCZ&1k#H8sXcYD;ZKtDC4B#HDBm1k;vO`q17{ZYcqSi>9$aK*={ zc*5XP?MiT|1WM)_6t4zN^Qb{nk~{jfChm`Kc2~z0_9^HuY3(MB0I;MlX}Q(V`6>II zytSOJ)E_VbCvUv(5kq|ahsUbnvs0T*NtAN@Z|uz2brSq&?pKBo0k!)_k5e?W6`fh#p$rBZLH)LSZbkUC%6 zSN9*(M-3`*QwMQU2fDpTxpHSJwFDC`SDz@=XMWU|){ErtGH%9vgn7r#PZaF4AsFYo zHyRe7%Xu-zNvnVVKB_-?>_0_XaD1Udt9!DPdLHxFFGz@AU)`Sis`&YR!uj6j<4k?F zQbRvC(1o6)L|1?1@+K;8Nq^;Cn5?|e#alDHMYWcpDQj(#kqc@`;E{~o8&%x%-G@%@t4 zZify%esd{8`b!yWoIFS!)kLKa9qA@b_Tn{N{Ym@RUni3*Pi z*Oe%BD`usgrpcG-A5I&c%QB(>v%&UL3NH6Iw?yW13TrdLxd&{Xi z1Z14Bavf_KCLDG^j2bX4Ne#F;p}?j4qutMj$D2B&Zim-&)t^JF*RMb`(3L2N?VgA9 zp%WA6D;KF@3k&Ek^VBfc`O4HhnOVblL8e^86V&iPD(zzk?PIVS?i!#>uf$D{iS%#k zb13y`_wVNZCuldnLJs9*1ZA9dWBNP&yu=<)=cjZ;_V?v1xqgNDi=FR@;JYwG>^|U1 zajO)@mK4U86xveCl>W{AkGI?J(BWq=>i>Y5;)K`vC+!l(*@fY8w%OGq|1KF{Ih1e> zaWlsERYMj6skoRm1Nj|E>M^dzzD~6AKg4<7vbFWlUo18OFRcY|4-h zLpxLF(oeRs6M7rtJ|-~{mmaGaqsUL{G`C8fV)sQU7jaO=Rx`VGjSWBk9%BQhD-Oa@ zC#lp)Ds&-^>Y?cgYUH%L)JWIus{3q1qSW>N7}6djeX}2ZGl{;Ls0Q7fT&-!bFrG1h zaey(v_+j26e}l;1p!v2R>d?curTyss>el_Wuh5P$$*F_ITTyR_DWDDny2i$Lh+95aM;2Ttu*(=%LpIGl%Y{gmgvglZ>USHCFLZ%Vv)(e0)u>`AZ3pI2%J zM%s$N{zKwvgRC_e2Zqca*x|GWhenGIDD_9oqc)99AB$K=F#kGzOyb;gkn!mSrCxPt zdNO1E%?Yi2_s2EIR>u@Z7eu8CO}l8(HNOu%GeM1;_KoOquI16awJGl~^7|$2_6My> zJ&keN?TO~TEB~O>Z!yl?XWDWJZTV}xw&fPatuIS=`}<10k8#pVm~)T#81>lyP;k5VVO8qHdferUe&1l`l!_)F}g66srs z^UeCuH8N3+4D?qcOOol+{nW^=G2dS6bQ?cfSp%IYudR~Tp;Hso=s>A!bV-S8^t58v zXxGz7)@6QM zrV8#-&5pb~Ulw+oqq_XqUN!iSe7vE{f8^s09sak;$B%SHii0+};JeN-{GmK{)Qi=G zm<6T6AS@^flr2`*@)gOgg?nc>xN3`{{{b*X*tc{w}+L*u_QVfw@&R z3t%)y6x>0Nv!l^KXP`BFU4aekD>Pi!;#1xt_TfT*hog?g9rEU?5EC__%Kb0~_J{PX8 zE>)T0I;X0#wyL6ZPN1g3#8RU!)%L-f8ki>83 zj#*S$rkg}b&Z=TWzX=Zkh*YWjrJN^pj*8B$%`ROQT(P3Grl6*@7GkJVV&(@bE-t5% ziYgXW!nb0-Gg9pGs;aIGR?mf1E(wrnVG5;+%bcQWO89(N@`42punm8KtTHlJ;YI8{#E8#scxLDh2n=VTL+@7t?@rvs7y&4dY@6qz+O86{UfmROHZWK}9L@ z{F9^e=HwSu(~4eHm z>RPTqEG#FTT1inb^=*565sSsj7oAsCRFYS|tcEKOl=?N@2IiLO_3<~_LlMN!&ee&RkDtBlgoV z^39a1zd26P-%M*d%zWE^femGLk@zpcNZKrZb-0y4FNUc}4acy+)cKcki2pi_M`QpfRX$lAEPCLe`0^%0hIjx93$!7jS+tjW28*aVZ{9vjJT&l6rqn8q07Ja zmwdvXN!NSA-@i6r|F>d4vGASA!HI>x{%_^*U!Tqin}9t_pRfsd|MhwMH>B{tyh#+~ znDv({Dn<_=`)vOY;s5zN-?{T7^`|?nJ2~j=@e9X)?HxMAMNB9cz4rCjyz27Tu6S)q z58sT(FC2Qa^%JGexYmS3RaWPm2w#5t-buC%vurrih8Z@TX2WzFrrFSI!&Do(ZFsbg zq4Rq-Y_;JVHauj*7j3xThR@ir#fH0W*lfecY`D#a57=<44Y%0vHXGh(!v-5V@vpJJ z12(L%VWAC|*wAmo3>&7~@N^q`ZRob)(O6UNzD)S82s(Gz_LdD>ZFtCr`)$}_!)6<9 zwc%zPZnEJj8y4EIz=jz%Ot)d04ZSu@wPCUi-8NJ67^?HGPnht$A)*?=`K|O{LVnuoY>z2TssI^0Ps5CKFk~7 z&j6E9R9ctjQiFiYFk8mDR0%L`2)ujz2%N`-=uO}Sz@=>5mx2pCG*YPtzy-dIkvNr? z^BzpW7?<(_zrZX6SED%3!bn;HVC-n(#NG|e!PJqi==^LH96vV#Cyp_AI&kh-(!#$V z*ou*~1b%OvDeq<=dcbs8fp=rX&lX_9cw?UkoMq!J!23@{R~d0W0PMtkB>6c_snalu z{G1LfJ{=x`&;*z;k>Y_T0#C&hh#%nBXaq~ZmjZWUq%6CE?_wkm9|6xzM=lThEZ{dW zLgzKWUt`42R^Z4plzNPp8@<4DFcNWNV zux2J@!A}4;->+am1XP&M*H9i5q}Ku zo3qhD1il7%6GrmC3HTbDjxy{;R_WCo@+mlQyB`@O@W+4y&nHgsrNA{92`lh+8yEOC zM)IaEpqerJ@t+R#V-A5A058J40bU3!!nA^y0H^06j|-jwtipT*UJZ=TC;!x4B9Lo1 zDj+X#0x!l$9+m+AhLL*z2v`SmOz0`F`cmq0Jn;ZeTS`9#KOOiOW+Ax1GcKp!flmVt zDB_F}96fnzCPw0~SfPi2)u3u>axM>fUYuQ9|L?9lY#vkz?5=hp9-90<9=Ys#%~1v4wH@lX5c3np~L6E zd#*6}y}-;0+8cfXz#n2H4=uoPRkSzoG~ksO$$tQNH%9zy0bT<$@m}yXz)vwP;GYAp zt2KBXFg9RtH*gb1>Pz6+LFyO(Gl36cWc=I)jJe7#FR%mSK9xAd?rPc!xWKqorXIb( zKC7uC?A^dTjFeH}6cji}|C$C|^G(WvAAvu_NdLMW*ol#{h`iJYjFiy}T#MO^|E<7d zn62PyEn4NTC7csuorkQM#|U%Z2AS?*lz+pd6%J23o!p~L)!x2w=fd_2H-x7ghel;ddJ2E zKJZK9U*J2xGGnR0`|mYl<^#ZA{Tf=4*1f>ZzcF))z(W|RFM-LwHMqcCm{$B3Y^7Y7 z_rPxf&fEt7cmiz(*l#=I2zWAZHb&~S8u&a$^0{B|M`<(o*$?dVn2FyDy!CNTeX-vR z{1Zm{y9J#5gu%0b7N!nA0`J=a9~}Gv;Q2eD8+ab@SGy=L_`Sf>c2j=vEMQI>x7rku!F9D8!#o%ec zGK}~an0d&w!A)nZ<0X~Kidx0O@_)*|RpHd&#F9hzx$e8d9Fzz$z2zzv)s?#tM zR_^J@y`#@*O9JJdkKh93uFO`(B7t%bM(hRdwsE-&Blk_jUZC775&r^*es1gqiVVK^ z5h(W^1Q#fG8w3|9_YedZ_%j=qy9jcRK4*h{2a#nJvb@yloP3GDZuz`pea_8lj%S3(5)7nyGI3GBTmuut#BUii0J*caT% z*bRKgB%m^W!5Bk+obSTB7)#w<-|pWs#!(55d-VgjkL&tQeT{D_*>P`v7yrcVe5d`D zZ_4C+Z{picB|G1@{f%)UBK8WypnY7|*zw*ytyUb!2MICckL$7Q+4ac)q+(wG`ecWC0}kY)#sXAF z`>!r*?^jwuUl)InzsA#kK-cASz>uW;KR(n9w;u!Pu<09@JD_fnpa$+ zAG1FAdv-;!=*OD>Y~oDmW7gNdy>P7bv2I`E#>Uy+d`H@)FI9=hu9OqiQUg-qjymOP z`0RqLMdLappR=Ab9NVcZr{KP%Di`Ex$TgAcB6|qs+zr`+d^0)k)TtBRql`D#4jG~z zfBbQco00Lwix;b`tSq%@(QqrGDctWnV5;OC?(?f?2&5Ie($%fK8K0I-d z$Y!g|dd4en#89hBk<7f!L)qTz_~E}IT+4;4S96t?;wO}v<>4W2H9bUCb7asC)>WQO z9oA>ATgoT$C{XhWhUo^WMT-{7$Hxcn>1e0?{ry!?5Z)Uc7N&VOc<^8~Y}hdM&_fTY zM;>`Z&3del8Z%~$8aHm7ii?X=NlADgE$qk4nKM=T;ipPt`qu_l(5+p8(%| zG1i^AIClg1F-7nNq@H>f@GAhH1NdElKMeR&PVg-O9~cRLF#&$!V)%!-@CyOIr%0(o zfIkNKF9H8G;LifS5b#%=;C)+SehVty!{AyvcOlj~Sbr701tmOOPsy?NO1>DZUQ1N6I}L5FS91E$ zHF(Txk<|fzJK$>pzBb@te~RD?iREr3z1k}oIatZ#iAr8fQ?g~flB0*N!K*rWe@X+K zNooq8$p>oNMdd^Ci|~$TsrNAU-V&4yeo9H=3MFY9l&s&U&)eGd53fG;Y8e*kX>>5mp-(ZbVc;bpY27cG2+7K-YL`mw#J zOM^vSNfdQ8P1H~8Mg4L}%HZzgT>(!H+za^o0N)hwEdl=k;Cs~*HN3s3#KEE#B%-Y}QF-e{9Y1spzPxF$ zmL}($!NI+QdIyE*TLW5qw`lI^*|Kk0g`nQyVPPR5;lTj`K_S*Q-d~$##<97l1xSXKwQs%mp8ECs`|AdLG?h*99QcP2J}4Z|@2TIU zzXP`ct%(BQtpPz11H;2Z!>x_jKtuNi4gPZHop&}KKpgp;FaM7~FV;roDp<(|J`WC! z2n!F72#xS4R{_txTI=?EM}&ljMubH4xxdl9jxNxHwUu|90id7l2kR~j*Q`C=fda3< zKiz)&9uZ)1L}++~CPL$A_z(Q8A?*W+LU=@kwNalw_3PIM5oOP(;32SEpTQct`}e+{Z&x*`$v{JOa801$C%aw??}FYlJl-EHt7NOPG+- z6c*g6cd&1Dm)Zjz56G*q5SS~+b89zWw_3NmxYX+h42fbycmM?H+Vh~Uo!fP+Rn7J8 zFgy(I4O#BgDLDArbE~y?(4Zc5YS!q29)hiGJuKu}|JGp2-Jl+K-BvS@&w~RXuHgn8 z{3CxLV1akkt24+N91+k1vR3vO&rRy*R!t>rfOD}6IkhzZ8GkMXZB)!s znJ<^B0xI}(H}+GEKlk8+4{Cp8R&?Jo-{X~Oz0~~JP_-l}SZ$gUs&bdjQeF4Kr+}U7 z_lc-s@EzzgOhfs?3ooeU%a^N_D_5%Y^mMgm%^K}1Y}~j}`-5-1@rI(W@X@YU)N=S6 zx$qVC?%k_C{P08V8=N{>piZ7VsZO0brOux}ufF^4JN4rah1xf`eEG8a_19lj+Er2O z;VT^a#mUb4HpN8O6%!rwa`9+Pbki}>Ey6^%R@IYDs=e$~gJqvelp`ulK3D7IH0JMX z^NjMvgc#`#cucm79{_w8zy|_89PlFmp9uJ;0lyOP8vy?v;0wy;ng9AJVBdfJl>d`{ zN+VU88Z~MJCBi;tL;h{#-on?{w>3Xm8Z~ln)U>sSTb(-h!yj(w>D{7*R}0^IZgpGT zh3iI5n|XPmZap^-Umsr|)!4JOw{Mf$zV%R{&Ruui-?(WDZ{Is=d*AQ4VX=6(_H}i= z(;G0Y?yhrJBliZaeeZB}tzD}|jXPV_t=p*j?TuPDxx=+KZ}_@-+*{M7rYGw9`ZlRm zgYEyt{kHnJx}#a`TD5$z4rtoqzG{u}6d+A-jsATa-{aNH$Jf`#3;3h|);>PXeSDhw zX!;r>S&*7G)t4%zF81PUq9S}{on25?mU!RPVST_U55xvhz&%%wBD*LH{{E?S8=&E_ z>#r}sYu9BBlV~; zIF671kwpHmU94`Zl*n5*WQxCK)v8s0!@RS-u(0r(@4x^4Tg*KtFI>2A8fC$yOP30< zEcrQN%Cr}XaKyCd4+I5kFYfLsrmxNux+J2F3$$9(n|t}A=x^*VpzRwu{ey2>**0FA98_v}Vnkbp{U?o;!C=u%}zb=luM9`SjCIHJ%tBjXTHY#EBE~ z*=L{WYtm#gd>;K7GI!~RAATr?-2H+!&;0!J&+_AsKVJOkqmN$y`s=R?(AQ6d0iFMX zzI6r;3kmy2@rOSp=&LLff0M~qlQ||P6MyoGrTNTjW@#V3A&%4u=&&x2962J))D4aYOX>%8hcNHI z|GuVyV+j2hjsy1UxrJMnaQzGJm+(1sxC3aYs{S^-a^;F(8q)Ib=jYdwa?H#zz`mJm z-@aWi<^rEt>oCWFV}gA(or(LtefxyEa_rbK{h2h-22kFpCmbW6ZFh-0xL+jew8-TvSB^kesQ*<-8vmU;ccwLO-n=t>_=T{Sg7MHa(B^Oq z$XC+Cu^{gJ%<=#7%P)22XY!o68GVwRrjD;z0MNg;)l$XDKDbn{Cz7z5h z_)i)z23_74=>QtyKS8{s1pD2GMB44tVuhW>Dy4?lC#5Ve=-9ENCuCtB>A*N>dJG*b z$xF%+`Cl0w~lrzdbb;Fd@3#K7oi3|h{ z;gJ76;5TXTKPb}egHjsWK^L%3F5Y>%I_+pxlExplI1PLJoiPpzsb{n;mC-?YcODZX zS1ieYKIgnZSlSuqH0%^~lr(%H5(XMVK|}5Z=Ni}j`~#jWyACl8fBNYs!8}tglLnIw z9hHrVp~abwUw-*T4!yooUY-#y%Mt_Rg^7V0v4_7A8Tz%z;1ePdq~TMCK0{`D8hxfs zf z4s4QFruLM~$^PfHiX+;{qf6MD4gJ7qSKCBFX*n2Ji z(6xp1hp2Og4nqsafb)U#m>61E5`Wss&9j3f=ZPMY1sYxk4e66g@lP%kdGtJJI3w~m z&_I2rO$vuiGWtv!j6RbFqtCQS-rF_)I7w74HKd+#eu1A=mPv!j73na#;!FoWlLn@( zDcxkljP8>2cn^7X8fci}FPDqX$tO@}(qIJ*h_T7vob;JCiTWG_U7$_!gH7W6Y;2NO zo=CG&{43fejX(VR1)V#0_Jofzk95#3vZTzA4*EPSNel0Bt~GucpK-pW&%pFXYB$+3 ztDCF`4cVY!9cb9GbfR1;gz!`$odun77!yCv&!EBh7+yO|fy;3p_Mi5`$ba|l-CJ@j zOs2jPZ{kMW4K1|&wD(-s&~9?B;@rlxbB>?94jMMk>Mpr6dWan~RMh8x!zQK01<8W( zy=8uEu*@A3EGdtL$a9k)mM=d!D5SyJ$I$u=o5WNZ{;>C2{(;Xz;!eC+5+~wKeITFB zn9#;M`^WT$NF(L{t@*v=P0+9nG;Ep)8lVf*XVO4@rcGK3yGj}slZJ7<<>|4YAtpp- zJr=5IAfEIwI6oU7qci3=q~FOuZ3gFH`Vq|Q)~yqp%_j6qO*Z4f@ zU0|vVS#uA26?Nh3{}tC7|2A#fbivV{c>GlRdHB(K95OO8WYC~Ng0n^PkAM6_5L1%p zpMPHC!}UG+O&T~CaGs!CF>?(=8fZ@`hnx$^qrK0C$l+Ir{}tK4X38}m1G+#TgZfOH zv}{@g(ZA{X3wwXhAQU>A@&j2MKZ!eW zpugmtNrTCT4wh_>nKEVCrfvOT>nBN*>0??2!yrOcZ*?;_49$(%WJE zE43_<2I>X(eTWb&K*3SxU!wv7^*eM8svrj2U_yNCWLE_LgP%@ZtJC z$AC1LOd8C(mupJ;*pz$X$&xZe+KhbhK7A_s+^{A8#NJaEoHJa+HN>spPq}BNEOEb? zG!ZxMIpge|*5BaZU!cM6OEG_7ik3KnTDSJe)^;e)G*YH4Wqs_YI*Rnue&TC>bzdfR-)9xJLj!oq=t81assJ;Jyd3w51vX1KPdg{#W-?)DXK0I$ z*X#c%?xa!UZ~TAodmd>pcG1vcXkbZx(>7u5*6Rey6z5uJ{t{PS6Mv44@gW%3q1;oJ z$aCrtY{nAcaVxl&;qNT}v=PqZQQ4S~F7C09963^OE?3L9;kk3kdXy!~I`4B1AnqnU zf;H00KY_c(pM9A1FXo^9ux9*%a$#&Y}qm`&*Znsq?@us z-J##aYsw7U<6Hon`3hdaaI1VL?o4|B!FgUJ{w9+KlW#O8qzPxD^?XGcBMfOHzLc#z z*iO=7aEE`o_7>&66zgk$_5Kg^ORs-1f6pT=H*|&4Z8ocGUH4^L-Nz?f5J|b?f;Ml&YkpMX#Xe&oR2tnlE++glJ^`3 z`T}Mgcukv6TT45JHHD6Afad=+?xaJ@zq4#qlyh@!^wzngtn-?6I2M$7@|iSJ)*(l~ z!ACfQvEsbSGZuejZX$j+OLwCJ&mjE2%9 z2co%36Z>+2u$UTqdV%`-@_cDTwv-`?xg5#=T(1 z6gnWbGZK5lAOEOPx)BbfwQ-FaHM(MLmk6CMragntc^UThEarmmV3&@=KhMBE**N&X zA*hcxu_#aY8--&K<6xYOd!d2Yzh%su@#3QwMe?yLhwmdXeUJLrOHE+IGtp-;?I&#{ z*Gt5K*~Bm$KL2m9s~2H&kHBue!G;+#WxSDbF2+~5C(iiLN0&qng7zxJdOc{Tv9Az? zy{BQsfxZ*ho}3?P*Etu_R@0ZIpTcMS%rpYAD#kn+Yh#Ru=NA~GVtj{jf5zCDu17rX zdvFbaHE2B63*$Kda$e&)m;KU@CQlsnYu~A~#nQiwmpzQVTgLksE8A4${It@~3}QLU zgYKW}LHY>H#DSUiotZr0{B_~&52M&yT zGJdY*5jZf`#uyLfkufU9IvFQ?2s(na&oL$*oX4^65|8iSjpN+RY;d5@L7vdJ&Y2ag zV||Rza37J0eKRxm%J?y3e$Mj9vn-6!FxJNy6Xnt8O$~a*^iMy?#1}cQ(oZw~o56(; z+*jsaU?%o68S}+=>0~x^%ozvDn5G=fVCFPl>|5!Z2q%*f-^z zB@^RqjFB*2$T-!O7ZYw8Gd%aRNKye}p1^_Ud8iYN*)kdW=~qmjK0Q7qC1o6aP-cS% z_f5zPCho5@*2EYGV`YppF}}e#8DmV0Z7@d0_|lBgrTK+9u|gcQJRQm!togkM&_!S|^M=`hyQh zW#doZ3~`7keD87?Z2{N&^v_8*aUl;_9?p!_aYM$d7`tW6kg?}gj(8z;g7Fc?3R4lI zGCW{s&NiB{Tck4ir*7f9z45UBow!`s2idJm9YVv9y6x*kq!S&kn^YDoLrN&a%||;t5-+t_f97r zh+|G1HEPtm`2MzxA3t921LKUO-n%esAM%|1Apg0(qb!gg#J^%Hz{-s^QB=X%Cv7+Zp$B{=u3={D;x;=xRQ5RZyuL;N^z(ROfMisri@)4#h>^57a2 z{>M4S5*e4k_e_QRuf!oSF;VlK_JH#s+cq-5zGxSWu40}jL0o1GWH}i=65cYSc;@M5 zYbp=&3cO!DcI?=97~|m{J-+ZS91F(RFfZ$V=ns(Z?4OxF8GSTUVy^lb{Com!twOxw z0{Z4s;ATn7A9avz(YGVNxtB{B zcDYulO49b1_6O(a$FaQv?8$S^r_Et(0q-o(F=pxo@na$%%pNcOWyVzKw}XZi=(MVR z6F=R*k!SLinRqa>Kh8&ZM}oEuJgZ9DDRUez@|twhCS&hq?H}x0_s@P{Yqb5Z3=iW2 z<2wg}?>p+fV)}*LbD}){iN1CJq}R;9lqJ&3HkoPjsB_e9(n%TP`5m6U!1n^QeYi!s z**B91>95FlXZ~{xm}z@y`#8>cCj{m10`|k6K^xpZxz)t)nz-F!rheVbzFilu5)XW5 z*QMk~Xo_HyrI)zj6J@^()s3T&uLhT4^cpVyu;Ga^g<;XTPt`3e!H$ zMXbS=1826uwK&&a+>7A4kLyl9tUI|!O`nQ*({3?w4Z}6m#(yUY+i*_jVPd(b!+iv< z*~mYR6XziMK}_493f2A=*B@MaaP321m+KAtif4pva2?(ccyRpi?in5DrVS$>PV7yW zEvf!`JxSl4emmCsoxzTT)U|^cfMx)i{=v7sG#D8GjD$&eeYZ zOsstziNtOu|1d9TyTzCs&kqpR$lUr_z2w}9BbuLFLp>R*`@dx5hq6aoPrJjh#CO*< zPid<;mS674kPUPC>hs(yr}dZpZ@j|pHye0-cSZYZv|p4P+HLw=91q%4XI%K1bGd9wR@ZM}!@DfqO0W3-wcGHFbzJq^*Q()J z=@s9-Rvm9N;*~|ed98+{CazHDc1KN%e(PFIyjzX#-Y_*pS@Aa%?_n8&x5o@p192UO zzkTqT>CNhe@C{w`KN=){Vi~}PNY(KVXq8Jb@FHE%-X#25R;-FwW6)YGeo-qLEyt@E zH4(LY>pJa}AGS-oA$P)iXn?#5hdbh;f>9?9Z+D48{pr9a3Rls(k0EG@PuQ9T@2`nc zlTl|h-W?Z>-YjaUO4grP`S18@t4mqmA-JE6n#3sqxW%H6_$sv-iudD019CE;qJSs+ zX6k@n`nuNsFx_vmQ@ic)rgi3ax+K53IqV7;@?ny$ACDF%I8itW%YaU(AFcbud$CnB z)E|KBF}fx>lK`HOiZP&i659OzJqw)aV0^LCf>EeCzx*_AgB)#hAD>YQqQF5#L4I-`mxBQ*eUq6)G^V?We=SnhfV`1f1h|j^pxlcmI?gp?-`XG z7C&X;_~;~0%jDRg(WCJ*y8fOqQ4^A*J$v=^Eo-|xa9R6KHGbE7Pv3I5_Vg_y8sI&B z4L^HD21N#igoF+3JA61kaHRO9>|+@x@cT|h8LpXbnUR^pGnE_OF^&8CRv%k^W_9su z*L3%E?{vTPe(A&0$EHt9pP#-YeO>yt^nK~a($Az9r@LmjXYiLBjsixlc3YkL>f)>= zS*x?wW#wjV%i5K-FY92|v8)qWXR?a2inEl>)#he%w^?l7wstl@TcE9D) zo!u_mFFP>1U-q`_W7);o?m2!r({dK)EXi4&vo0q$XIBnriKLd}RVNwKGEy_t?Wre9`1&BsSG$7UvEPRmTqBxC-Y z{>y>?T^wlEG`Rc7$mx^DPK+Pfv2E9p3HoE(=xNcl@2VZyzgqQsG`>w1&loz*?QHQ5 zTrqRKX|={h#m3`JXbIDsS=zL2W5F-0<43!@TP9D6Y2(K`wPWKFCMHd?Bt@G~$p*@%TY*tJUJ~Z}Boccy)&nw^#t&HY#b%lo9P7 zvG}9Ww#k!6c_(>!w@DtI6q_(xQ`3(>soI?~D50-N$4{})_3DNSr*+N$0X(oM>Hq)$ literal 93012 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 diff --git a/libs/common/bin/chardetect.exe b/libs/common/bin/chardetect.exe deleted file mode 100644 index 17242a806b0c0f64941749f6dd2160db387ba30a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93026 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 diff --git a/libs/common/bin/easy_install-3.7.exe b/libs/common/bin/easy_install-3.7.exe deleted file mode 100644 index ba897f334d14c187286e16a1ab2e31b03abf6bc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93035 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 diff --git a/libs/common/bin/mid3cp b/libs/common/bin/mid3cp deleted file mode 100644 index 8a773e56..00000000 --- a/libs/common/bin/mid3cp +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.mid3cp import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/mid3cp.exe b/libs/common/bin/mid3cp.exe new file mode 100644 index 0000000000000000000000000000000000000000..e3235f666537a7d7e5566c268855e0cd38250d01 GIT binary patch literal 108396 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 Kbz?1E;eP=1(=mbo literal 0 HcmV?d00001 diff --git a/libs/common/bin/mid3iconv b/libs/common/bin/mid3iconv deleted file mode 100644 index 332f6b70..00000000 --- a/libs/common/bin/mid3iconv +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.mid3iconv import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/mid3iconv.exe b/libs/common/bin/mid3iconv.exe new file mode 100644 index 0000000000000000000000000000000000000000..c67cd5b2ffcd56080b2d304930d4860d2db0fa65 GIT binary patch literal 108399 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 N7CL6#P@$Tx@IMx6GVK5W literal 0 HcmV?d00001 diff --git a/libs/common/bin/mid3v2 b/libs/common/bin/mid3v2 deleted file mode 100644 index 1bf2d13d..00000000 --- a/libs/common/bin/mid3v2 +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.mid3v2 import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/mid3v2.exe b/libs/common/bin/mid3v2.exe new file mode 100644 index 0000000000000000000000000000000000000000..a7c245b47ecf0b38a8d0c50a827a04ec7b86f0c0 GIT binary patch literal 108396 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 Kbz?1E;eP;!Vlept literal 0 HcmV?d00001 diff --git a/libs/common/bin/moggsplit b/libs/common/bin/moggsplit deleted file mode 100644 index f43d1360..00000000 --- a/libs/common/bin/moggsplit +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.moggsplit import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/moggsplit.exe b/libs/common/bin/moggsplit.exe new file mode 100644 index 0000000000000000000000000000000000000000..ecf55502106dffc9bea7444648e2d04713db9450 GIT binary patch literal 108399 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 Owa_u^h6>emh5rH1Xf-JS literal 0 HcmV?d00001 diff --git a/libs/common/bin/mutagen-inspect b/libs/common/bin/mutagen-inspect deleted file mode 100644 index 746b414e..00000000 --- a/libs/common/bin/mutagen-inspect +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.mutagen_inspect import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/mutagen-inspect.exe b/libs/common/bin/mutagen-inspect.exe new file mode 100644 index 0000000000000000000000000000000000000000..e41e38b85f71417c7f33c475e3815ed5e9be8387 GIT binary patch literal 108405 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 Nh3;85RH>ya{11%xH$eaZ literal 0 HcmV?d00001 diff --git a/libs/common/bin/mutagen-pony b/libs/common/bin/mutagen-pony deleted file mode 100644 index a289a988..00000000 --- a/libs/common/bin/mutagen-pony +++ /dev/null @@ -1,16 +0,0 @@ -#!C:\Python\3.7\python.exe -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - -from mutagen._tools.mutagen_pony import entry_point - - -if __name__ == "__main__": - sys.exit(entry_point()) diff --git a/libs/common/bin/mutagen-pony.exe b/libs/common/bin/mutagen-pony.exe new file mode 100644 index 0000000000000000000000000000000000000000..efff7a2b6ca4ba719d2ce8511f3d663a5265514d GIT binary patch literal 108402 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 literal 0 HcmV?d00001 diff --git a/libs/common/bin/pbr.exe b/libs/common/bin/pbr.exe deleted file mode 100644 index e7eab92cf2cc657d9c4770725f485ca0a00e1287..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93016 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 diff --git a/libs/common/bin/srt.exe b/libs/common/bin/srt.exe deleted file mode 100644 index 90e6494dc3f873c9cd9407c4e8b8e511a743d47d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93018 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 diff --git a/libs/common/bin/subliminal.exe b/libs/common/bin/subliminal.exe deleted file mode 100644 index 280ce315cf27e10110fc508884a54196c8cdf645..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93030 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

    oF5%wc?0lVIdiCvTMwKQ}m1M4pzSU2C#ISwFq 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
    oHJaZa%&cRgbKqQScY`o5It4F}rqGD>-kDeG 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 literal 154226 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
    Cbk((@9-7tXbJf|5eCh;duD zw1czWKy2Ia+gR}Jh%w~(mG2aL_wBby&`JMI<8gmYoIrXdi8*ttNRQ@Hd1?7|S#T-$ z`VLQezGkoHgwLp^ICD*TNWZ0e&N{CaZw-&u^iHi#g7~Kz_4Bq)TO$UIMtkLxxhoT1 zEvQ=msgG+}`9P-73Ey3y?T^N9b>+7|hD(LdwmIkgo1w#1>x8L}pX^e7W5wtbRi->9 zb6Av&#o12Q*w1qOViszjRtzlfV6|V30=FsK*K0*?eU8wUjslH;&HjZo-9}tdl!B#J zPT~hC_(^>lxcCklyJs7MC$`tEOUv~ZdrE^Y-;@S1NMz+moLQp#otKL3`yp$bM>G1y zQtl|T37{<-jDwI0wMN%0uTeCXKR=ud4DH=|l;)J)j-T9ONBET{*_tegXt!7#rn%O3 z&S2!0U+qNSvaeV3xUbGFtesqumY&+J@!sg>Sf2^8jm#cZ-5qzLq2wz0Zygso*k$*G z2roXh>W`G)ls*25G*HY1 z+rRmlXt--e-N-q{>*FH-l;u3GWcDQ9v_*llSTAICD3(iGK^{8v04 z!(VVT@t5L0td@_PXe*4)uN>37UJ57}29wiu0kRb!3rKEauP|Pq$x(!m(@`NsD}E{J zW1w+H2P#nn6jlH-pgSIK5U8{Q8u0;*$bm-KfQ$ynq2zRMKt|j65FIJG`7S5EPZp@4 z1sdWxrnS5gP$EIh|u~JRTHKwt%UwKu=+yCnX@`0CFTb-6^Yl?6QnvWKu^oy!*spHkg)bddjzB%5XSeBj3_)oc>2N+1827gvCaS?#Er|OFha`m5n))4(={&dZxjX#+>CH%vRhU@ ztYhC$U%!E8Dv`GxpSIUtI?m9;>bi3CGqY0^x@H6p22`FhB9Wg0n$9$y=2IP>9NV)@ z*Sj7E(^_3Bwqz42-^QKQB~&GZ2`j?+4TLgt5u! z97Togi-)clwo))cygPj%pa9@KA!I&}2Rk_#sg5 zst~wzoi3zvw>&&bF+}*+9mrnQZ8*&FdCCg$%S2Sjr23#={qnN0w}1O6lkzgY z5obHM9ecF7v@qprX+ifge~DwSS*4laPaT-O#lG=fAg>WSubu`v4)r_Y=mf18j}t~> z4D63FPY)w>w)c#5EbhM*c9_c3CU&dRyN)cFT#LfAZ@)MzwsKI}>@0{kXzG3eYi!yv z5w8EK);dfxDPK8jFzkP7Qg7_%Z`H4rQn<;v+CH3g(zk8N+9BJ+(YH0mNVPlBwJY~G zkJGkVd&+1#QqEbQ0v+dJfzxe(ZIXX_Q8?&q_*VZ*wdo-YkdYieujV(&o>Qn#yVRX zRT+g=XWU-Y%@^7GE6NSF>krMiWT>pkHmYPEG8v7mC$}H7R=D{GNS`*{w@$;J!L5U@ z+NWIySzC4PGZoIfdo47Mt}=ehQ(J#_ugnnt*xPXajKPK*Y}6Q)i_Z#tYPS3M1Y(Lw`R`#;KwH85u#|LQc=(o7{PyZ_9Yd+b;Ttp$1Q6U(s8xHdn!Lx!+|a zRy4z_Q!7>?a5Y8Er6}X48Pwvw_svq2w;d};6>M8Ppwt|vf<8TPO=e2^^jTRvw+f%# zA!(R$F7A$)W6iEVeBMxb1isnN%Y$>fXDj-r%!+hU#-PMFXl@8c;dEf)nzIyoeCSkt z@u(G1!f}>m6F0`6cN|F)dC8`pHTK1%xI(g1;H+D zg8Ye#tn-#9nf*(Z2Sl~}cJaVbm3AL+RHvI;!er_&|DRsvc#IanBmfNIx2Q(FE+E+{ z1`iyLroKKC?11P)EeEN`grVbf;M0WMq`oJVkV5Ghs&}JaYj6&Y1Z;92p%SxNH zmR~XNEPD=IIsW5YbfccL*W?Ij-qKwia1a24r-4ty$-22(`hc-|2$-ZHQ#rl_ifz=R zV*`%Rfu=t8fVP$nY_|U2X0z#mt^Wiza%K&T?*}%5)#t0tm1YGomsIBgg0-YNUz!cX z9OZP?`WJ;D09oqMOU72z0Y$0)`R;-E4sp>VMl616KEWlSEeiBXDvn zbc@|JC`P(tnCC5J{+${Z(|Drt3fg{SyRj?rQa;a22eYuJqb=jTd`SM$W@_IM$k-B|( zYpj3!{0)40*Kz(q|IQ3~Of178)z{J9?3B;DVC4xpf04i;Lu#-gqbHiI;%HRzusU5exArJfgicpB70DB;p7#3>t|iGrsBOPSI!q4 z6>V826ykSj@s{fpb1}R+-7PB78M<(MqyD_*xf2$$3JIGD-QH*>mjj90F(iuW8k03z zf93bVVmi5RTKZE?Ov~h6Wiy~C{;UPO4eavX>#^zR>axl6Xb}I5nVgxOmeArdKwQ{4 zJQK)}ia9F$XpXPNa9Vx?dXd2DWe~$oBa?OB5~^%tto>}#_jn8SgFX5?Q@u)1*+_;j z-5gO2_kj4gFcN7yIAj~o<$9i6+(tN1rL1L$JkJ(17a!BLM& z#iZ|LVu>TF{`0(R?41{_DP9L|?Hjh^o>HLaO0Taak^W*IyBDNaOMMc#mh>#!=3K6i z-wSfURK6GdC+coYR2r=ohpK!z9jM0~^tF5k_;ELlePj;3X}%l~a^kOj|Ef#XjTtWc zE~5v8tFe(BRdp^G8+J#@rc(BDw+94+ema{R{XOM9F*Y-0vC2J&Kmqqj_jOfQym&4^ zxZa(64I{j*j1Vk5Fq6l|-~JG{K?FPlvFt5A)nJ#A>8v^^ax1nlL5gF!Y$4IontE|9 z+kb4eIk>!1yvZc8r!9uuuBP2p>Z4O5yXaMTm|MYWu|W_CzjPhxS{FQAoph{*9w^F& z@~MtvHLJMu=6I~Fcr_hzV%Fv4DtYNREO`_yO86x$jX8t}ebp0b^zN2({Y0qBcfR2@ zQqVv9Hx*YsHWp_#Lq0f-EmlxJP*=*f{<5ps;e+u=0t3a#hl<=~oIjiUziAVNAanMz zvO8_98Yfc;6Vm~%nWoegM#pwzMrVoY?RsUo?bJslx4KO^b$Rbk`1Fd8wdMI_2GzCg zwqIjl7ulBz_SoI45smIfv97F9Xr21X&KhyJu3NQJOhv}#KII!6?n7^jynj{aAO1Qh z8|P9K*FwWuv(#o#(MIzWky-o>6^5*7k9??_D=OJjzm4|jlb}-6D=PtQxG`Gf3Mb$e zK4@ex?UOWqZ}z@E6iMLP%FP;75!;j-XKtZAwqq$K-g&aHT}&CW;1wG^NJXC@o)X$t zjhG8Gdo6VQGb8K+&fdY-b7b|`o6;@>5t8UKhHQ~NxH_XnNdyC2#&`o<&CGFT#PTGA zc0V{1ZfHMK+?DqT#vtnpnFnX~BUMqf^$1!DU~Jvft+|q5YzfZ2jVl)Xnl7bJBI_*4 z64}I=uo4Hn`~M5dR{!6+vh{&L^-4pDL>5;|i4T|%eU|0&|B}!D zhY%oy1eL$uYSAWmr6m8ELgxLiS%LxQTs@M(VPKcvXg>Ww-(tkJwip2d`peSl|8eLT zTO`bHwsZ&ot#B1j1K8rn|2P}Noe5C75y&xDHlO{T>kYRMX9gAfKR1eM9@g(WuL8YW zpnlI~vh&XpzzgAa77g`tc@0J_H(Se-OuDPV!pI&=4Yz;h7hU+NLKrN?!0cwa&j0qv z!9yqpx-hHx?hd)JZ%h9+Wx-ZhA^*UExzj1WcfkA|&kT1jHqm|5fYxGkmT&D{LVqEGi>CZP05?>2b#d$ja(GU%!VyyUCZhiE zKa9+~3Ra_bE|2kRn(F+eLSj~X0R(rY z9tQbIzYV65%d(tqBM)2fpu4Q!7__K*ht}gHww%xwi=AFWoZg^(a~c_1&q#)HfdcKo z@ZGrQ6H;#s&P(l2+icxK2BFE#uR%_P_LfWB_jEQ#U$HA`u^G0j!i1BW(ykZmnTa21 zZt}3ZrA(zjsB+oEfgGxnCarKCZ8&22{_Np$@IdEONzvaZ?mIx z!Goit6Dxi%d8=l|Ti099k}GoTEU3|Q>mppGfO^MjQ!+w3zo5qOQc*R5L zGzz9r)VrH@$N@=9Yie34UaQ>;$gm#0+Tu$c#Zxs{q{KPdpmjCrC#HGJEEX!mhLF5p;ca6tG||eYdgrX{KG`T+OtuZ+>WC2^N-fbkgECl*S;si zDow)m$S3Z;96W9Dm7f<-P8wR##v6Qjqquz9Y6QH~101i|uY-r5Ch&@#ZdWYVLsfpj z2iGj2^@+zf_5yQ{745lXtaab9*p8Iq2NE^N6LXvdFfTslk`U+SH097gpfzgc&QYrJL`~4jAL7Lmevz-K|4x6hJ@3L(kEL{_VAByz(N`YcvWC6qa?O>mpKEf)85=NG zrrh_%daW(u<-ix&@T0_oRd`{g+#{fI2FGmNC}!@9n-H1psgFcW@iJs_$`rPyu` zq<|b-*eBkR`KF+D{_~-p3_7L1B$i#4#^DUlM=XxqcQU+ws_|pls}x%7D)DV@iz|0} z417LTb*<|{olA(+on55%4;0Ajd8P?^n_DCVxMtc68E%-4R|y2<7k{`Qr;<$Ue=E(b zgPYWWFJx?=**uwwev$HRNaEVuSDhS2Y2B41qwQ7{TM}|78p9%l>EQ+5$Ft%;G=w`x z3Lfmj=*r*q7G4GQ@W zhW*L3eL6srCsUT{6$_G&Y8OliyJtzD^?qC3BqO@(&~1o{SD0)QJtL>uXUB^Zy}a*+ z2c0^gO(>p`7ag1cRRhsawk%aJQH9}xDu2RpGstCELO9>MKX|GVIj#~Vt`aS-@=aVN zMqDMUIHMnkWvypWhv>uq!sJt ze30eBG&kzz%x*yGHJ>KNOqDm|RBDPs9@n$uRHCB8?z0^4=3HdV!?44N7=}oY(5Iq) z&QcN%`^;^Fn{@rUcbohjavgg&`7z5|+gH;J*XZ)aoILOHdpIAEA+Mru(1E6`ZgKL> z+`u2`zv1}3+xlLU`t%XQ4;KY|%9yF)AX5kYUh|D8-7QWfab^l!J4#@ZCopLjnA8>t zOri#eKoWWZf(CQSK47W;0bQklrQ-K<3Spn=k@s`9;;+?%V?NhWo$0XvU`SYhNO$5Y_7 znI2q%=Lm7lKpmsxZ4Ltt%ZmU0{-(uMxq13A{@+i&&8( zBe-==TKYJJgZ=$Kf}^MC0J+B7VP`s3daQ9iF6;d*zsLJDw&C6hUKNEu%Z%Rlw>7EY z$~Y`Cp|hfZ+4e%(ME(T@GJIENYkO-a|HEgHh>cIeg3l!QZME#zBfZ+yn-wYt$L*{9 zeJnql>w))vHg51dcag9`c41XjN-^J=-dO5_HpIKfX%Xv$4}za!q_2$`$)~h2ulM0~i9Z58!nKK7i{^5q z^~ENr&_;8xzM!lcDd5}36C$7d+-eUD9BPhcR@Xb?N2oaN|8ByXL3jgC1$OcxhV>Dd z1mRqZ+8B>DS~{E_J(HPN9t)y!_$vILo;+R)UvNZ_viPhwa7m zzgu|Q+gePi7Zxv9+i}t{J_lsPBCE3e?-t%!pWOf5!b@0~n(^-z-im&Sdc)3J&DPs3 ziqRXV2^N8b#M%`@M=eJOZaqh6r<(Jay>07^Q;3VFz0-~{TI^2bnsVAx!4|6oe|4oO zBc7wT?%T-Ab7R-h;yse@D8E)rb2lCBNoP@n9mvy!UhYS!>{n3G;2rks%Snc=@H;mP(QnLqqL256%qH zX_bMUUTaxo8>_8-jHM01RE85bniDUI@b@Cra!5hFo=mTU2wq!zsdIkakVguRU_e13 zDK++F8XbB+IutxQ)Ezt8`tik;WVRbBwjC9N<0R{mJH_5A0`HNSR6!yl~Yva ztLm<|K~TsHiVM4#{|}|<>|7?rNqR9w;buf`$sTdWd|-QBdj%}M?D5R<)VcFFzkVQB zK={#}Bil`;&zZ4L>nBQB`oL(0u{TMcTVs!iVogD1y4y+ruabq-ONQ0^rjzO1dPjy; z1**S3zJH2n2dDX~J%wvj+nzEeZVI|f5k*d)7oJ9hVTr*KE#lvW!HA!_-;atZ=wZcA z@ajYkzBvh@b>D*-Snu>q1q~KDq4?oiUIV3D+Pwmu3`-|}Atu>R z9t(M06OV>-?CrZ`>uQAzR&{m-)SgF1>>QrszJA%_>(xxv;g(Ma7&v4xxFUe2H7iq22ey(=V0@U5Ts3V zU}6Qp3qTZrGyo+4ng9#|miXCy;(E7a3*lEkA826<_e`0d2)Vm@n$=3{DTem^?-AM!jt|&~ zP*yv{l63Xb}44{cG=-0 z+)fjQqtALJY#9O)8bo~(jjVB5#jIr6_IfMy!{t|QBpO&V z6Ld4ESxPHg4%M_zslHY&x$5e{Y{)I*En4WDQ&jYx3dZYH94z#x+b8O}brCjmm4U3F4@#LqU$jhct+7%jzBfb=y<{N<@-hIxv z+=;Rp(J_i(F=;liUo1bXKK^kRUe`YAJbF6g&ALudQ|ixCQPFg#=IyZl^Kl8@nBwtk zR!020(QkXM&IYO4{v9b*(e2);$#jsvt!@J9a=O_Li+%#1e<|hDJUg{hy}A&5WznVX zRCA|narTDXl)v#V$mR4%HsgxCI(l(7&OhVKG?bP2agd34d7;6N`kNlR_jbLZV~awo zdTB(8I&WXfC2ft%&Elt>rGwm&8&}FS@Y9ydyH;8HBXdpOo zSx~RMbgya&$o${a9(CL5z|$Ul`sI;Mb|ui9>hyVyi1ApTbCU*fmQ_v>Q>v>4E;twx}8J5BkQ>{UmHF{bX&dz!UbF7&f>YHbG3@j zy(n#~0h_ARy7(M}6@Fy_?~KxRbs3j4Z-v{aRT^LZ6=vVYLMGqLT$U#<+9C1DfssdU z9$r|h+TxjfhqhBY)DWRWT-BYjrr%q2(*VZLwT`#SoWo2`Z=}b6-Yy+?wT5q4Y+Gv8 zSR=tFw|9kZt%K8{>Bvq-8KL>mq);5%Q8sq=6oYDG_U5I#)_I>pg4Xd76#eBal0`v_ zj``}VW&I~4Tz#~4B;dCg$ttsGe*Ll~z6KQ>H~mc0@f~&sbUUN7_K{V7*jIOn-FJ6C z2~vC=Ti)K0`Kq3ZZ3WpM`YT~m`YU7Db?BYWxE2x_Z-9ipZP>DL-+|eFlj7>j^ z$Ig9AjkJSJgj>ThJr+=p$yjmxDvF5eA)%t*olunX_t|h97bFwnS>NI~b8rlzh^9mm zrEGgOo9cILh2*LI4bJ%Vq)!!al*a$gIa>7cm$t*Ktch*0&g9$xrb%UdzOeSwmJ!8X z;pygT1!DYI$OO(QJArVUyeUN7Pp&@TMO9j%Y|l>NXHbaj^ghVfSv}_!wsOohNWDIs zD;;m)=yxq|m?GF^PI$vXUD~j<*#)g*0h92WmFO$Jr zA*qm=mcO(%eC&eqKJgeTDVG1J<$sib%BJjNeCmC2#MZVyWUJ*jt&LF9H3e}Vtuirj zUI((d!ypQS{JL|u{Ir+hmlQWDK&kpio&BTy{!!EbbuuUqJUYtte@tCfR2*HiMH4*1 z-7SRR?iMTrhoFPIySo$IAvnPj+}+&??yiHoGq?Hvm-|q4_Nm&nPo3_yCQMIvr?8O7 z?}B|7nx$|YcE<}UKf~`G_bz;75gnHvzw86_w)rqRp1g8ar`sXw0g0dW;yXm8wsy2F%BL%a@+dv7VNdJ))5e*HxV~C12#m8MyQ|^Cf zpIi>753oBm+Ib!t}Br#qTnW5~TsD{te!0O;*yhAiH=$;(s$UwQTB{AL< znc<4hbI0oN3M57c-BaTnSt$1nCB}z@heska()|;&@p&ZK9I{Myw8sV?e#JR*Qtn%; z8w{^@(rBj-$Z7d;q&aXIjouP ztV|3(WXB0WQ9eOQ+`>d2zsFm|)GI@IfcZW)NSGr*;R{6-(>W*zEv4fh_X0!d`A*^% zHS(AkZ&8T3I#kbyv};f&AnpZ@(i6$e;SxLYm;rB57i!3mxq1RB>r;kDvWuEMHQ^;u z zZ?S>7+Ew2O(mhBL66cLV>4`6K%NBWTj<-0^T-~5=vckk>+3_>d%G`Z&AeQ9rN66QA6~NOA-2yK zui2p~w2u&9F-NfO8pQo%`R)$*XS||ie`Ek+d;?*vf$YZvPg@qu%!`XdY+tSy2d;2D zW?$E7*$8AHy(D^?@-dbbn026}Qk}K+>5&oe_Dh{NKhaK-%fv&9?vbE;`3l63FX>UW z(fPKKYZ%1(j>YOhE?S!mwu)aBQt^+jDl+=I`CKjlf4hd($+ z&R^QFfcQ+RK^+$h_J-TShkIRB(ONa9-x@IIOQJVb$6O0_T~pQPL)E0h?WwFI_h4*@3^D& z6wCu5ajon0yekM8=^=K=C~2DHBmEo9cN_b&H&Hp_(T5h>($WcwNp(iM8~E|Gcbn`_ zV)LLPcvQ;qkd%OX_&9ddIOdN<(}RxEgKPv5_NbzX@f>2E!GYDEsW1|YU6Y?G)TPau zbr$a(>)z@9nRS$l*B*H}uxqIbF~BU8d0@aqHJT5jnPs?dj+#WT=p9 zb(=F5Y}FNgcEYf>YGCEQG&J+ssikaohw=7D1SugO%Iq>a6Ci&TO3CwP;$0J=hH)ry3~bZ<}8`*iG*!!H%J*V2?j(` z_R+9ApXRCr{kDjYU_b4>0iBGBE54_a%nC>B@w1@}C=4dOn{Yzmt@mGq?BSBL-C#!K zwf%Q#+dD5YP4ynaxhkJ~iPe3!J^*P1APonk19oWzyDl+{{Certy|-{;@8L(}i2`?N zvw+|{P-Dw29K{oAX>Wx=7}FRUCI4(_gqFunMqPZD+3Thmtl0&>WPstpY*p9#FL^0!Rl=`CY#U)II_gMZ)}BVj@*?sk&wPdlDZU=An?S2|D?+nXHR|q0TRE+x+=?rL5Az+i0@VkF7iMXg zbMFg@>fbX-5R!0MnRq%?1>RhwJ=W7^%dzvXHjUm|De3lHQ4QU0O-KANpMy`I&d;2) z>S?d%DZxcqlwHSnbi=LNWA`#1RplRy)q3bned>}biC`Z(*g7yBw2a~0hfSQMD(P55lPaf9>;#&lz!=%(z5Dw+p4c5sy4Zp)P_E-rl?LOZLeM_ zg-}aI$i6O#XQ4;oHZr1Y<(EXrB_%H&{dZ-QZ(O#9eIkn}NzcoRpBo<56bh<~$}Cnv zAr^gW^{PzvU79ZsMH2JxL#;W+@1#*N& zp*Hj{5y&prZxkN1%1$ zsb~4JXQY)rvAnQ^R@ER^AWY(%X()O)>VNt%OO}=1`(Fa>o|AM^oFgk42I7S@{x$tPxzQF-&xUo7lyeFbZYH) zhMM~o4K)cQX@~k*+Q4`N%k7pBX83Hda6~{cSZdHtFplx1yT?fO4Swyw*q{{+< zD_ZW#HfM01qjHQT1h&ZP7yZjHU3wmE)7B2us95b$c!b$JCh4X|c!Ka2H9x&Qrqpwg zDRneVc+q)bud18!7X4f)hUq^++O=*<4A)>aq9_QdBc2q0h#D1R4?RZqCU*Gr`OjWU zH}O5rJ?vgv;7`;l;;?-n0Wn{k$P!xATlp1<5O(y_u9BV7eiT0 z>z!HQInI9)bAF}vc~L|WcffmPqKFKD_^I%m0GT=IQcu2bXU*2iQ)!^^l;}SiLCRJ# z^E@+5q0ar%C^O6i!93aT1mT=4hg3_w`AI-Pr<*X|Bp^FKU3f~aHdI>3KR?&(Lqar+ zw9G#m8UgY2RCa7@gDKV8aA_?d;QbcB0RkF8!15nW7FD@M=dTD(`@}{QXiFwQvqN}F zuQs$K!V;$v=eNLj4-_g{iS$$_e4xezsL2LIq9oGYqTcu%0Ds;L(-T>++UB?H_oXPH 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 Rs}CAN=Sk9x^OX$ee*qyL&5r;8 diff --git a/libs/common/dateutil/zoneinfo/rebuild.py b/libs/common/dateutil/zoneinfo/rebuild.py index 78f0d1a0..684c6586 100644 --- a/libs/common/dateutil/zoneinfo/rebuild.py +++ b/libs/common/dateutil/zoneinfo/rebuild.py @@ -3,7 +3,7 @@ import os import tempfile import shutil import json -from subprocess import check_call +from subprocess import check_call, check_output from tarfile import TarFile from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME @@ -23,11 +23,9 @@ def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): for name in zonegroups: tf.extract(name, tmpdir) filepaths = [os.path.join(tmpdir, n) for n in zonegroups] - try: - check_call(["zic", "-d", zonedir] + filepaths) - except OSError as e: - _print_on_nosuchfile(e) - raise + + _run_zic(zonedir, filepaths) + # write metadata file with open(os.path.join(zonedir, METADATA_FN), 'w') as f: json.dump(metadata, f, indent=4, sort_keys=True) @@ -40,6 +38,30 @@ def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): shutil.rmtree(tmpdir) +def _run_zic(zonedir, filepaths): + """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. + + Recent versions of ``zic`` default to ``-b slim``, while older versions + don't even have the ``-b`` option (but default to "fat" binaries). The + current version of dateutil does not support Version 2+ TZif files, which + causes problems when used in conjunction with "slim" binaries, so this + function is used to ensure that we always get a "fat" binary. + """ + + try: + help_text = check_output(["zic", "--help"]) + except OSError as e: + _print_on_nosuchfile(e) + raise + + if b"-b " in help_text: + bloat_args = ["-b", "fat"] + else: + bloat_args = [] + + check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) + + def _print_on_nosuchfile(e): """Print helpful troubleshooting message diff --git a/libs/common/guessit/__main__.py b/libs/common/guessit/__main__.py index 9894180a..fad196d6 100644 --- a/libs/common/guessit/__main__.py +++ b/libs/common/guessit/__main__.py @@ -142,7 +142,7 @@ def main(args=None): # pylint:disable=too-many-branches if options.get('yaml'): try: - import yaml # pylint:disable=unused-variable + import yaml # pylint:disable=unused-variable,unused-import except ImportError: # pragma: no cover del options['yaml'] print('PyYAML is not installed. \'--yaml\' option will be ignored ...', file=sys.stderr) diff --git a/libs/common/guessit/__version__.py b/libs/common/guessit/__version__.py index f5913166..e505897b 100644 --- a/libs/common/guessit/__version__.py +++ b/libs/common/guessit/__version__.py @@ -4,4 +4,4 @@ Version module """ # pragma: no cover -__version__ = '3.0.3' +__version__ = '3.1.1' diff --git a/libs/common/guessit/api.py b/libs/common/guessit/api.py index 3bf5aae2..8e306340 100644 --- a/libs/common/guessit/api.py +++ b/libs/common/guessit/api.py @@ -82,6 +82,19 @@ def properties(options=None): return default_api.properties(options) +def suggested_expected(titles, options=None): + """ + Return a list of suggested titles to be used as `expected_title` based on the list of titles + :param titles: the filename or release name + :type titles: list|set|dict + :param options: + :type options: str|dict + :return: + :rtype: list of str + """ + return default_api.suggested_expected(titles, options) + + class GuessItApi(object): """ An api class that can be configured with custom Rebulk configuration. @@ -228,5 +241,23 @@ class GuessItApi(object): ordered = self.rebulk.customize_properties(ordered) return ordered + def suggested_expected(self, titles, options=None): + """ + Return a list of suggested titles to be used as `expected_title` based on the list of titles + :param titles: the filename or release name + :type titles: list|set|dict + :param options: + :type options: str|dict + :return: + :rtype: list of str + """ + suggested = [] + for title in titles: + guess = self.guessit(title, options) + if len(guess) != 2 or 'title' not in guess: + suggested.append(title) + + return suggested + default_api = GuessItApi() diff --git a/libs/common/guessit/backports.py b/libs/common/guessit/backports.py index 3e94e27a..c149a6b5 100644 --- a/libs/common/guessit/backports.py +++ b/libs/common/guessit/backports.py @@ -4,7 +4,7 @@ Backports """ # pragma: no-cover -# pylint: disabled +# pylint: skip-file def cmp_to_key(mycmp): """functools.cmp_to_key backport""" diff --git a/libs/common/guessit/config/options.json b/libs/common/guessit/config/options.json index 11b17271..da7c7030 100644 --- a/libs/common/guessit/config/options.json +++ b/libs/common/guessit/config/options.json @@ -1,18 +1,19 @@ { "expected_title": [ - "OSS 117" + "OSS 117", + "This is Us" ], "allowed_countries": [ "au", - "us", - "gb" + "gb", + "us" ], "allowed_languages": [ + "ca", + "cs", "de", "en", "es", - "ca", - "cs", "fr", "he", "hi", @@ -20,7 +21,9 @@ "it", "ja", "ko", + "mul", "nl", + "no", "pl", "pt", "ro", @@ -28,18 +31,50 @@ "sv", "te", "uk", - "mul", "und" ], "advanced_config": { "common_words": [ + "ca", + "cat", "de", - "it" + "he", + "it", + "no", + "por", + "rum", + "se", + "st", + "sub" ], "groups": { "starting": "([{", "ending": ")]}" }, + "audio_codec": { + "audio_channels": { + "1.0": [ + "1ch", + "mono" + ], + "2.0": [ + "2ch", + "stereo", + "re:(2[\\W_]0(?:ch)?)(?=[^\\d]|$)" + ], + "5.1": [ + "5ch", + "6ch", + "re:(5[\\W_][01](?:ch)?)(?=[^\\d]|$)", + "re:(6[\\W_]0(?:ch)?)(?=[^\\d]|$)" + ], + "7.1": [ + "7ch", + "8ch", + "re:(7[\\W_][01](?:ch)?)(?=[^\\d]|$)" + ] + } + }, "container": { "subtitles": [ "srt", @@ -59,9 +94,10 @@ "avi", "divx", "flv", - "mk3d", + "iso", "m4v", "mk2", + "mk3d", "mka", "mkv", "mov", @@ -77,12 +113,11 @@ "ram", "rm", "ts", + "vob", "wav", "webm", "wma", - "wmv", - "iso", - "vob" + "wmv" ], "torrent": [ "torrent" @@ -255,7 +290,6 @@ ], "subtitle_prefixes": [ "st", - "v", "vost", "subforced", "fansub", @@ -297,12 +331,12 @@ }, "release_group": { "forbidden_names": [ - "rip", + "bonus", "by", "for", "par", "pour", - "bonus" + "rip" ], "ignored_seps": "[]{}()" }, @@ -311,6 +345,7 @@ "23.976", "24", "25", + "29.970", "30", "48", "50", @@ -329,6 +364,7 @@ "progressive": [ "360", "480", + "540", "576", "900", "1080", @@ -342,8 +378,8 @@ "website": { "safe_tlds": [ "com", - "org", - "net" + "net", + "org" ], "safe_subdomains": [ "www" @@ -351,12 +387,200 @@ "safe_prefixes": [ "co", "com", - "org", - "net" + "net", + "org" ], "prefixes": [ "from" ] + }, + "streaming_service": { + "A&E": [ + "AE", + "A&E" + ], + "ABC": "AMBC", + "ABC Australia": "AUBC", + "Al Jazeera English": "AJAZ", + "AMC": "AMC", + "Amazon Prime": [ + "AMZN", + "Amazon", + "re:Amazon-?Prime" + ], + "Adult Swim": [ + "AS", + "re:Adult-?Swim" + ], + "America's Test Kitchen": "ATK", + "Animal Planet": "ANPL", + "AnimeLab": "ANLB", + "AOL": "AOL", + "ARD": "ARD", + "BBC iPlayer": [ + "iP", + "re:BBC-?iPlayer" + ], + "BravoTV": "BRAV", + "Canal+": "CNLP", + "Cartoon Network": "CN", + "CBC": "CBC", + "CBS": "CBS", + "CNBC": "CNBC", + "Comedy Central": [ + "CC", + "re:Comedy-?Central" + ], + "Channel 4": "4OD", + "CHRGD": "CHGD", + "Cinemax": "CMAX", + "Country Music Television": "CMT", + "Comedians in Cars Getting Coffee": "CCGC", + "Crunchy Roll": [ + "CR", + "re:Crunchy-?Roll" + ], + "Crackle": "CRKL", + "CSpan": "CSPN", + "CTV": "CTV", + "CuriosityStream": "CUR", + "CWSeed": "CWS", + "Daisuki": "DSKI", + "DC Universe": "DCU", + "Deadhouse Films": "DHF", + "DramaFever": [ + "DF", + "DramaFever" + ], + "Digiturk Diledigin Yerde": "DDY", + "Discovery": [ + "DISC", + "Discovery" + ], + "Disney": [ + "DSNY", + "Disney" + ], + "DIY Network": "DIY", + "Doc Club": "DOCC", + "DPlay": "DPLY", + "E!": "ETV", + "ePix": "EPIX", + "El Trece": "ETTV", + "ESPN": "ESPN", + "Esquire": "ESQ", + "Family": "FAM", + "Family Jr": "FJR", + "Food Network": "FOOD", + "Fox": "FOX", + "Freeform": "FREE", + "FYI Network": "FYI", + "Global": "GLBL", + "GloboSat Play": "GLOB", + "Hallmark": "HLMK", + "HBO Go": [ + "HBO", + "re:HBO-?Go" + ], + "HGTV": "HGTV", + "History": [ + "HIST", + "History" + ], + "Hulu": "HULU", + "Investigation Discovery": "ID", + "IFC": "IFC", + "iTunes": "iTunes", + "ITV": "ITV", + "Knowledge Network": "KNOW", + "Lifetime": "LIFE", + "Motor Trend OnDemand": "MTOD", + "MBC": [ + "MBC", + "MBCVOD" + ], + "MSNBC": "MNBC", + "MTV": "MTV", + "National Geographic": [ + "NATG", + "re:National-?Geographic" + ], + "NBA TV": [ + "NBA", + "re:NBA-?TV" + ], + "NBC": "NBC", + "Netflix": [ + "NF", + "Netflix" + ], + "NFL": "NFL", + "NFL Now": "NFLN", + "NHL GameCenter": "GC", + "Nickelodeon": [ + "NICK", + "Nickelodeon" + ], + "Norsk Rikskringkasting": "NRK", + "OnDemandKorea": [ + "ODK", + "OnDemandKorea" + ], + "PBS": "PBS", + "PBS Kids": "PBSK", + "Playstation Network": "PSN", + "Pluzz": "PLUZ", + "RTE One": "RTE", + "SBS (AU)": "SBS", + "SeeSo": [ + "SESO", + "SeeSo" + ], + "Shomi": "SHMI", + "Spike": "SPIK", + "Spike TV": [ + "SPKE", + "re:Spike-?TV" + ], + "Sportsnet": "SNET", + "Sprout": "SPRT", + "Stan": "STAN", + "Starz": "STZ", + "Sveriges Television": "SVT", + "SwearNet": "SWER", + "Syfy": "SYFY", + "TBS": "TBS", + "TFou": "TFOU", + "The CW": [ + "CW", + "re:The-?CW" + ], + "TLC": "TLC", + "TubiTV": "TUBI", + "TV3 Ireland": "TV3", + "TV4 Sweeden": "TV4", + "TVING": "TVING", + "TV Land": [ + "TVL", + "re:TV-?Land" + ], + "UFC": "UFC", + "UKTV": "UKTV", + "Univision": "UNIV", + "USA Network": "USAN", + "Velocity": "VLCT", + "VH1": "VH1", + "Viceland": "VICE", + "Viki": "VIKI", + "Vimeo": "VMEO", + "VRV": "VRV", + "W Network": "WNET", + "WatchMe": "WME", + "WWE Network": "WWEN", + "Xbox Video": "XBOX", + "Yahoo": "YHOO", + "YouTube Red": "RED", + "ZDF": "ZDF" } } -} \ No newline at end of file +} diff --git a/libs/common/guessit/options.py b/libs/common/guessit/options.py index 50ee4235..8fa6825c 100644 --- a/libs/common/guessit/options.py +++ b/libs/common/guessit/options.py @@ -128,7 +128,7 @@ class ConfigurationException(Exception): """ Exception related to configuration file. """ - pass + pass # pylint:disable=unnecessary-pass def load_config(options): @@ -153,7 +153,7 @@ def load_config(options): cwd = os.getcwd() yaml_supported = False try: - import yaml # pylint: disable=unused-variable + import yaml # pylint:disable=unused-variable,unused-import yaml_supported = True except ImportError: pass @@ -252,7 +252,7 @@ def load_config_file(filepath): try: import yaml with open(filepath) as config_file_data: - return yaml.load(config_file_data) + return yaml.load(config_file_data, yaml.SafeLoader) except ImportError: # pragma: no cover raise ConfigurationException('Configuration file extension is not supported. ' 'PyYAML should be installed to support "%s" file' % ( diff --git a/libs/common/guessit/rules/common/formatters.py b/libs/common/guessit/rules/common/formatters.py index 434c20be..2a64dee9 100644 --- a/libs/common/guessit/rules/common/formatters.py +++ b/libs/common/guessit/rules/common/formatters.py @@ -25,7 +25,7 @@ def _potential_before(i, input_string): :return: :rtype: bool """ - return i - 2 >= 0 and input_string[i] in seps and input_string[i - 2] in seps and input_string[i - 1] not in seps + return i - 1 >= 0 and input_string[i] in seps and input_string[i - 2] in seps and input_string[i - 1] not in seps def _potential_after(i, input_string): diff --git a/libs/common/guessit/rules/common/validators.py b/libs/common/guessit/rules/common/validators.py index 0e79b989..0d0eb3eb 100644 --- a/libs/common/guessit/rules/common/validators.py +++ b/libs/common/guessit/rules/common/validators.py @@ -28,7 +28,7 @@ def int_coercable(string): return False -def compose(*validators): +def and_(*validators): """ Compose validators functions :param validators: @@ -49,3 +49,26 @@ def compose(*validators): return False return True return composed + + +def or_(*validators): + """ + Compose validators functions + :param validators: + :type validators: + :return: + :rtype: + """ + def composed(string): + """ + Composed validators function + :param string: + :type string: + :return: + :rtype: + """ + for validator in validators: + if validator(string): + return True + return False + return composed diff --git a/libs/common/guessit/rules/match_processors.py b/libs/common/guessit/rules/match_processors.py new file mode 100644 index 00000000..0b49372f --- /dev/null +++ b/libs/common/guessit/rules/match_processors.py @@ -0,0 +1,20 @@ +""" +Match processors +""" +from guessit.rules.common import seps + + +def strip(match, chars=seps): + """ + Strip given characters from match. + + :param chars: + :param match: + :return: + """ + while match.input_string[match.start] in chars: + match.start += 1 + while match.input_string[match.end - 1] in chars: + match.end -= 1 + if not match: + return False diff --git a/libs/common/guessit/rules/processors.py b/libs/common/guessit/rules/processors.py index cced26a5..5b018140 100644 --- a/libs/common/guessit/rules/processors.py +++ b/libs/common/guessit/rules/processors.py @@ -34,7 +34,9 @@ class EnlargeGroupMatches(CustomRule): for match in matches.ending(group.end - 1): ending.append(match) - return starting, ending + if starting or ending: + return starting, ending + return False def then(self, matches, when_response, context): starting, ending = when_response diff --git a/libs/common/guessit/rules/properties/audio_codec.py b/libs/common/guessit/rules/properties/audio_codec.py index a2566bce..815caff9 100644 --- a/libs/common/guessit/rules/properties/audio_codec.py +++ b/libs/common/guessit/rules/properties/audio_codec.py @@ -3,9 +3,8 @@ """ audio_codec, audio_profile and audio_channels property """ -from rebulk.remodule import re - from rebulk import Rebulk, Rule, RemoveMatch +from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled @@ -23,7 +22,9 @@ def audio_codec(config): # pylint:disable=unused-argument :return: Created Rebulk object :rtype: Rebulk """ - rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]).string_defaults(ignore_case=True) + rebulk = Rebulk()\ + .regex_defaults(flags=re.IGNORECASE, abbreviations=[dash])\ + .string_defaults(ignore_case=True) def audio_codec_priority(match1, match2): """ @@ -61,7 +62,9 @@ def audio_codec(config): # pylint:disable=unused-argument rebulk.string('PCM', value='PCM') rebulk.string('LPCM', value='LPCM') - rebulk.defaults(name='audio_profile', disabled=lambda context: is_disabled(context, 'audio_profile')) + rebulk.defaults(clear=True, + name='audio_profile', + disabled=lambda context: is_disabled(context, 'audio_profile')) rebulk.string('MA', value='Master Audio', tags=['audio_profile.rule', 'DTS-HD']) rebulk.string('HR', 'HRA', value='High Resolution Audio', tags=['audio_profile.rule', 'DTS-HD']) rebulk.string('ES', value='Extended Surround', tags=['audio_profile.rule', 'DTS']) @@ -70,17 +73,19 @@ def audio_codec(config): # pylint:disable=unused-argument rebulk.string('HQ', value='High Quality', tags=['audio_profile.rule', 'Dolby Digital']) rebulk.string('EX', value='EX', tags=['audio_profile.rule', 'Dolby Digital']) - rebulk.defaults(name="audio_channels", disabled=lambda context: is_disabled(context, 'audio_channels')) - rebulk.regex(r'(7[\W_][01](?:ch)?)(?=[^\d]|$)', value='7.1', children=True) - rebulk.regex(r'(5[\W_][01](?:ch)?)(?=[^\d]|$)', value='5.1', children=True) - rebulk.regex(r'(2[\W_]0(?:ch)?)(?=[^\d]|$)', value='2.0', children=True) + rebulk.defaults(clear=True, + name="audio_channels", + disabled=lambda context: is_disabled(context, 'audio_channels')) rebulk.regex('7[01]', value='7.1', validator=seps_after, tags='weak-audio_channels') rebulk.regex('5[01]', value='5.1', validator=seps_after, tags='weak-audio_channels') rebulk.string('20', value='2.0', validator=seps_after, tags='weak-audio_channels') - rebulk.string('7ch', '8ch', value='7.1') - rebulk.string('5ch', '6ch', value='5.1') - rebulk.string('2ch', 'stereo', value='2.0') - rebulk.string('1ch', 'mono', value='1.0') + + for value, items in config.get('audio_channels').items(): + for item in items: + if item.startswith('re:'): + rebulk.regex(item[3:], value=value, children=True) + else: + rebulk.string(item, value=value) rebulk.rules(DtsHDRule, DtsRule, AacRule, DolbyDigitalRule, AudioValidatorRule, HqConflictRule, AudioChannelsValidatorRule) diff --git a/libs/common/guessit/rules/properties/bit_rate.py b/libs/common/guessit/rules/properties/bit_rate.py index 391f1d2f..d279c9f1 100644 --- a/libs/common/guessit/rules/properties/bit_rate.py +++ b/libs/common/guessit/rules/properties/bit_rate.py @@ -69,4 +69,6 @@ class BitRateTypeRule(Rule): else: to_rename.append(match) - return to_rename, to_remove + if to_rename or to_remove: + return to_rename, to_remove + return False diff --git a/libs/common/guessit/rules/properties/bonus.py b/libs/common/guessit/rules/properties/bonus.py index c4554cd0..54087aa3 100644 --- a/libs/common/guessit/rules/properties/bonus.py +++ b/libs/common/guessit/rules/properties/bonus.py @@ -26,7 +26,8 @@ def bonus(config): # pylint:disable=unused-argument rebulk = rebulk.regex_defaults(flags=re.IGNORECASE) rebulk.regex(r'x(\d+)', name='bonus', private_parent=True, children=True, formatter=int, - validator={'__parent__': lambda match: seps_surround}, + validator={'__parent__': seps_surround}, + validate_all=True, conflict_solver=lambda match, conflicting: match if conflicting.name in ('video_codec', 'episode') and 'weak-episode' not in conflicting.tags else '__default__') diff --git a/libs/common/guessit/rules/properties/container.py b/libs/common/guessit/rules/properties/container.py index 77599509..0f1860af 100644 --- a/libs/common/guessit/rules/properties/container.py +++ b/libs/common/guessit/rules/properties/container.py @@ -44,7 +44,8 @@ def container(config): rebulk.regex(r'\.'+build_or_pattern(torrent)+'$', exts=torrent, tags=['extension', 'torrent']) rebulk.regex(r'\.'+build_or_pattern(nzb)+'$', exts=nzb, tags=['extension', 'nzb']) - rebulk.defaults(name='container', + rebulk.defaults(clear=True, + name='container', validator=seps_surround, formatter=lambda s: s.lower(), conflict_solver=lambda match, other: match diff --git a/libs/common/guessit/rules/properties/episode_title.py b/libs/common/guessit/rules/properties/episode_title.py index d429c3e7..ece8921d 100644 --- a/libs/common/guessit/rules/properties/episode_title.py +++ b/libs/common/guessit/rules/properties/episode_title.py @@ -10,6 +10,7 @@ from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch, RenameMatch, POST_PRO from ..common import seps, title_seps from ..common.formatters import cleanup from ..common.pattern import is_disabled +from ..common.validators import or_ from ..properties.title import TitleFromPosition, TitleBaseRule from ..properties.type import TypeProcessor @@ -133,8 +134,7 @@ class EpisodeTitleFromPosition(TitleBaseRule): def hole_filter(self, hole, matches): episode = matches.previous(hole, - lambda previous: any(name in previous.names - for name in self.previous_names), + lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') @@ -179,8 +179,7 @@ class AlternativeTitleReplace(Rule): predicate=lambda match: 'title' in match.tags, index=0) if main_title: episode = matches.previous(main_title, - lambda previous: any(name in previous.names - for name in self.previous_names), + lambda previous: previous.named(*self.previous_names), 0) crc32 = matches.named('crc32') @@ -249,7 +248,7 @@ class Filepart3EpisodeTitle(Rule): if season: hole = matches.holes(subdirectory.start, subdirectory.end, - ignore=lambda match: 'weak-episode' in match.tags, + ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: @@ -292,7 +291,8 @@ class Filepart2EpisodeTitle(Rule): season = (matches.range(directory.start, directory.end, lambda match: match.name == 'season', 0) or matches.range(filename.start, filename.end, lambda match: match.name == 'season', 0)) if season: - hole = matches.holes(directory.start, directory.end, ignore=lambda match: 'weak-episode' in match.tags, + hole = matches.holes(directory.start, directory.end, + ignore=or_(lambda match: 'weak-episode' in match.tags, TitleBaseRule.is_ignored), formatter=cleanup, seps=title_seps, predicate=lambda match: match.value, index=0) if hole: diff --git a/libs/common/guessit/rules/properties/episodes.py b/libs/common/guessit/rules/properties/episodes.py index 97f060a2..345c785d 100644 --- a/libs/common/guessit/rules/properties/episodes.py +++ b/libs/common/guessit/rules/properties/episodes.py @@ -11,12 +11,13 @@ from rebulk.match import Match from rebulk.remodule import re from rebulk.utils import is_iterable +from guessit.rules import match_processors +from guessit.rules.common.numeral import parse_numeral, numeral from .title import TitleFromPosition from ..common import dash, alt_dash, seps, seps_no_fs from ..common.formatters import strip -from ..common.numeral import numeral, parse_numeral from ..common.pattern import is_disabled -from ..common.validators import compose, seps_surround, seps_before, int_coercable +from ..common.validators import seps_surround, int_coercable, and_ from ...reutils import build_or_pattern @@ -29,17 +30,12 @@ def episodes(config): :return: Created Rebulk object :rtype: Rebulk """ + # pylint: disable=too-many-branches,too-many-statements,too-many-locals def is_season_episode_disabled(context): """Whether season and episode rules should be enabled.""" return is_disabled(context, 'episode') or is_disabled(context, 'season') - rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE).string_defaults(ignore_case=True) - rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker']) - - episode_max_range = config['episode_max_range'] - season_max_range = config['season_max_range'] - def episodes_season_chain_breaker(matches): """ Break chains if there's more than 100 offset between two neighbor values. @@ -57,8 +53,6 @@ def episodes(config): return True return False - rebulk.chain_defaults(chain_breaker=episodes_season_chain_breaker) - def season_episode_conflict_solver(match, other): """ Conflict solver for episode/season patterns @@ -76,7 +70,6 @@ def episodes(config): if (other.name == 'audio_channels' and 'weak-audio_channels' not in other.tags and not match.initiator.children.named(match.name + 'Marker')) or ( other.name == 'screen_size' and not int_coercable(other.raw)): - return match if other.name in ('season', 'episode') and match.initiator != other.initiator: if (match.initiator.name in ('weak_episode', 'weak_duplicate') @@ -87,21 +80,6 @@ def episodes(config): return current return '__default__' - season_words = config['season_words'] - episode_words = config['episode_words'] - of_words = config['of_words'] - all_words = config['all_words'] - season_markers = config['season_markers'] - season_ep_markers = config['season_ep_markers'] - disc_markers = config['disc_markers'] - episode_markers = config['episode_markers'] - range_separators = config['range_separators'] - weak_discrete_separators = list(sep for sep in seps_no_fs if sep not in range_separators) - strong_discrete_separators = config['discrete_separators'] - discrete_separators = strong_discrete_separators + weak_discrete_separators - - max_range_gap = config['max_range_gap'] - def ordering_validator(match): """ Validator for season list. They should be in natural order to be validated. @@ -135,65 +113,18 @@ def episodes(config): lambda m: m.name == property_name + 'Separator') separator = match.children.previous(current_match, lambda m: m.name == property_name + 'Separator', 0) - if separator.raw not in range_separators and separator.raw in weak_discrete_separators: - if not 0 < current_match.value - previous_match.value <= max_range_gap + 1: - valid = False - if separator.raw in strong_discrete_separators: - valid = True - break + if separator: + if separator.raw not in range_separators and separator.raw in weak_discrete_separators: + if not 0 < current_match.value - previous_match.value <= max_range_gap + 1: + valid = False + if separator.raw in strong_discrete_separators: + valid = True + break previous_match = current_match return valid return is_consecutive('episode') and is_consecutive('season') - # S01E02, 01x02, S01S02S03 - rebulk.chain(formatter={'season': int, 'episode': int}, - tags=['SxxExx'], - abbreviations=[alt_dash], - children=True, - private_parent=True, - validate_all=True, - validator={'__parent__': ordering_validator}, - conflict_solver=season_episode_conflict_solver, - disabled=is_season_episode_disabled) \ - .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)@?' + - build_or_pattern(episode_markers + disc_markers, name='episodeMarker') + r'@?(?P\d+)', - validate_all=True, - validator={'__parent__': seps_before}).repeater('+') \ - .regex(build_or_pattern(episode_markers + disc_markers + discrete_separators + range_separators, - name='episodeSeparator', - escape=True) + - r'(?P\d+)').repeater('*') \ - .chain() \ - .regex(r'(?P\d+)@?' + - build_or_pattern(season_ep_markers, name='episodeMarker') + - r'@?(?P\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ - .chain() \ - .regex(r'(?P\d+)@?' + - build_or_pattern(season_ep_markers, name='episodeMarker') + - r'@?(?P\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ - .regex(build_or_pattern(season_ep_markers + discrete_separators + range_separators, - name='episodeSeparator', - escape=True) + - r'(?P\d+)').repeater('*') \ - .chain() \ - .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)', - validate_all=True, - validator={'__parent__': seps_before}) \ - .regex(build_or_pattern(season_markers + discrete_separators + range_separators, - name='seasonSeparator', - escape=True) + - r'(?P\d+)').repeater('*') - - # episode_details property - for episode_detail in ('Special', 'Pilot', 'Unaired', 'Final'): - rebulk.string(episode_detail, value=episode_detail, name='episode_details', - disabled=lambda context: is_disabled(context, 'episode_details')) - def validate_roman(match): """ Validate a roman match if surrounded by separators @@ -206,117 +137,203 @@ def episodes(config): return True return seps_surround(match) + season_words = config['season_words'] + episode_words = config['episode_words'] + of_words = config['of_words'] + all_words = config['all_words'] + season_markers = config['season_markers'] + season_ep_markers = config['season_ep_markers'] + disc_markers = config['disc_markers'] + episode_markers = config['episode_markers'] + range_separators = config['range_separators'] + weak_discrete_separators = list(sep for sep in seps_no_fs if sep not in range_separators) + strong_discrete_separators = config['discrete_separators'] + discrete_separators = strong_discrete_separators + weak_discrete_separators + episode_max_range = config['episode_max_range'] + season_max_range = config['season_max_range'] + max_range_gap = config['max_range_gap'] + + rebulk = Rebulk() \ + .regex_defaults(flags=re.IGNORECASE) \ + .string_defaults(ignore_case=True) \ + .chain_defaults(chain_breaker=episodes_season_chain_breaker) \ + .defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], + formatter={'season': int, 'episode': int, 'version': int, 'count': int}, + children=True, + private_parent=True, + conflict_solver=season_episode_conflict_solver, + abbreviations=[alt_dash]) + + # S01E02, 01x02, S01S02S03 + rebulk.chain( + tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ + .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)@?' + + build_or_pattern(episode_markers + disc_markers, name='episodeMarker') + r'@?(?P\d+)')\ + .repeater('+') \ + .regex(build_or_pattern(episode_markers + disc_markers + discrete_separators + range_separators, + name='episodeSeparator', + escape=True) + + r'(?P\d+)').repeater('*') + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ + .regex(r'(?P\d+)@?' + + build_or_pattern(season_ep_markers, name='episodeMarker') + + r'@?(?P\d+)').repeater('+') \ + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ + .regex(r'(?P\d+)@?' + + build_or_pattern(season_ep_markers, name='episodeMarker') + + r'@?(?P\d+)') \ + .regex(build_or_pattern(season_ep_markers + discrete_separators + range_separators, + name='episodeSeparator', + escape=True) + + r'(?P\d+)').repeater('*') + + rebulk.chain(tags=['SxxExx'], + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + disabled=is_season_episode_disabled) \ + .defaults(tags=['SxxExx']) \ + .regex(build_or_pattern(season_markers, name='seasonMarker') + r'(?P\d+)') \ + .regex('(?PExtras)', name='other', value='Extras', tags=['no-release-group-prefix']).repeater('?') \ + .regex(build_or_pattern(season_markers + discrete_separators + range_separators, + name='seasonSeparator', + escape=True) + + r'(?P\d+)').repeater('*') + + # episode_details property + for episode_detail in ('Special', 'Pilot', 'Unaired', 'Final'): + rebulk.string(episode_detail, + private_parent=False, + children=False, + value=episode_detail, + name='episode_details', + disabled=lambda context: is_disabled(context, 'episode_details')) + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator', 'episodeMarker', 'seasonMarker'], - validate_all=True, validator={'__parent__': seps_surround}, children=True, private_parent=True, + validate_all=True, + validator={'__parent__': and_(seps_surround, ordering_validator)}, + children=True, + private_parent=True, conflict_solver=season_episode_conflict_solver) - rebulk.chain(abbreviations=[alt_dash], + rebulk.chain(validate_all=True, + conflict_solver=season_episode_conflict_solver, formatter={'season': parse_numeral, 'count': parse_numeral}, - validator={'__parent__': compose(seps_surround, ordering_validator), + validator={'__parent__': and_(seps_surround, ordering_validator), 'season': validate_roman, 'count': validate_roman}, disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'season')) \ - .defaults(validator=None) \ + .defaults(formatter={'season': parse_numeral, 'count': parse_numeral}, + validator={'season': validate_roman, 'count': validate_roman}, + conflict_solver=season_episode_conflict_solver) \ .regex(build_or_pattern(season_words, name='seasonMarker') + '@?(?P' + numeral + ')') \ .regex(r'' + build_or_pattern(of_words) + '@?(?P' + numeral + ')').repeater('?') \ .regex(r'@?' + build_or_pattern(range_separators + discrete_separators + ['@'], name='seasonSeparator', escape=True) + r'@?(?P\d+)').repeater('*') + rebulk.defaults(abbreviations=[dash]) + rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P\d+)' + r'(?:v(?P\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P\d+))?', # Episode 4 - abbreviations=[dash], formatter={'episode': int, 'version': int, 'count': int}, disabled=lambda context: context.get('type') == 'episode' or is_disabled(context, 'episode')) rebulk.regex(build_or_pattern(episode_words, name='episodeMarker') + r'-?(?P' + numeral + ')' + r'(?:v(?P\d+))?' + r'(?:-?' + build_or_pattern(of_words) + r'-?(?P\d+))?', # Episode 4 - abbreviations=[dash], validator={'episode': validate_roman}, - formatter={'episode': parse_numeral, 'version': int, 'count': int}, + formatter={'episode': parse_numeral}, disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) rebulk.regex(r'S?(?P\d+)-?(?:xE|Ex|E|x)-?(?P' + build_or_pattern(all_words) + ')', tags=['SxxExx'], - abbreviations=[dash], - validator=None, - formatter={'season': int, 'other': lambda match: 'Complete'}, + formatter={'other': lambda match: 'Complete'}, disabled=lambda context: is_disabled(context, 'season')) # 12, 13 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P\d{2})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?P[x-])(?P\d{2})').repeater('*') + .regex(r'(?P[x-])(?P\d{2})', abbreviations=None).repeater('*') # 012, 013 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'0(?P\d{1,2})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?P[x-])0(?P\d{1,2})').repeater('*') + .regex(r'(?P[x-])0(?P\d{1,2})', abbreviations=None).repeater('*') # 112, 113 rebulk.chain(tags=['weak-episode'], - formatter={'episode': int, 'version': int}, name='weak_episode', disabled=lambda context: context.get('type') == 'movie' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode'], name='weak_episode') \ .regex(r'(?P\d{3,4})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?P[x-])(?P\d{3,4})').repeater('*') + .regex(r'(?P[x-])(?P\d{3,4})', abbreviations=None).repeater('*') # 1, 2, 3 - rebulk.chain(tags=['weak-episode'], formatter={'episode': int, 'version': int}, + rebulk.chain(tags=['weak-episode'], disabled=lambda context: context.get('type') != 'episode' or is_disabled(context, 'episode')) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['weak-episode']) \ .regex(r'(?P\d)') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?P[x-])(?P\d{1,2})').repeater('*') + .regex(r'(?P[x-])(?P\d{1,2})', abbreviations=None).repeater('*') # e112, e113, 1e18, 3e19 - # TODO: Enhance rebulk for validator to be used globally (season_episode_validator) - rebulk.chain(formatter={'season': int, 'episode': int, 'version': int}, - disabled=lambda context: is_disabled(context, 'episode')) \ + rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'(?P\d{1,2})?(?Pe)(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?Pe|x|-)(?P\d{1,4})').repeater('*') + .regex(r'(?Pe|x|-)(?P\d{1,4})', abbreviations=None).repeater('*') # ep 112, ep113, ep112, ep113 - rebulk.chain(abbreviations=[dash], formatter={'episode': int, 'version': int}, - disabled=lambda context: is_disabled(context, 'episode')) \ + rebulk.chain(disabled=lambda context: is_disabled(context, 'episode')) \ .defaults(validator=None) \ .regex(r'ep-?(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?Pep|e|x|-)(?P\d{1,4})').repeater('*') + .regex(r'(?Pep|e|x|-)(?P\d{1,4})', abbreviations=None).repeater('*') # cap 112, cap 112_114 - rebulk.chain(abbreviations=[dash], - tags=['see-pattern'], - formatter={'season': int, 'episode': int}, + rebulk.chain(tags=['see-pattern'], disabled=is_season_episode_disabled) \ - .defaults(validator=None) \ + .defaults(validator=None, tags=['see-pattern']) \ .regex(r'(?Pcap)-?(?P\d{1,2})(?P\d{2})') \ .regex(r'(?P-)(?P\d{1,2})(?P\d{2})').repeater('?') # 102, 0102 rebulk.chain(tags=['weak-episode', 'weak-duplicate'], - formatter={'season': int, 'episode': int, 'version': int}, name='weak_duplicate', conflict_solver=season_episode_conflict_solver, disabled=lambda context: (context.get('episode_prefer_number', False) or context.get('type') == 'movie') or is_season_episode_disabled(context)) \ - .defaults(validator=None) \ + .defaults(tags=['weak-episode', 'weak-duplicate'], + name='weak_duplicate', + validator=None, + conflict_solver=season_episode_conflict_solver) \ .regex(r'(?P\d{1,2})(?P\d{2})') \ .regex(r'v(?P\d+)').repeater('?') \ - .regex(r'(?Px|-)(?P\d{2})').repeater('*') + .regex(r'(?Px|-)(?P\d{2})', abbreviations=None).repeater('*') - rebulk.regex(r'v(?P\d+)', children=True, private_parent=True, formatter=int, + rebulk.regex(r'v(?P\d+)', + formatter=int, disabled=lambda context: is_disabled(context, 'version')) rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator']) @@ -325,18 +342,23 @@ def episodes(config): # detached of X count (season/episode) rebulk.regex(r'(?P\d+)-?' + build_or_pattern(of_words) + r'-?(?P\d+)-?' + build_or_pattern(episode_words) + '?', - abbreviations=[dash], children=True, private_parent=True, formatter=int, + formatter=int, + pre_match_processor=match_processors.strip, disabled=lambda context: is_disabled(context, 'episode')) - rebulk.regex(r'Minisodes?', name='episode_format', value="Minisode", + rebulk.regex(r'Minisodes?', + children=False, + private_parent=False, + name='episode_format', + value="Minisode", disabled=lambda context: is_disabled(context, 'episode_format')) rebulk.rules(WeakConflictSolver, RemoveInvalidSeason, RemoveInvalidEpisode, SeePatternRange(range_separators + ['_']), EpisodeNumberSeparatorRange(range_separators), - SeasonSeparatorRange(range_separators), RemoveWeakIfMovie, RemoveWeakIfSxxExx, - RemoveWeakDuplicate, EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, - RemoveWeak, RenameToAbsoluteEpisode, CountValidator, EpisodeSingleDigitValidator, RenameToDiscMatch) + SeasonSeparatorRange(range_separators), RemoveWeakIfMovie, RemoveWeakIfSxxExx, RemoveWeakDuplicate, + EpisodeDetailValidator, RemoveDetachedEpisodeNumber, VersionValidator, RemoveWeak(episode_words), + RenameToAbsoluteEpisode, CountValidator, EpisodeSingleDigitValidator, RenameToDiscMatch) return rebulk @@ -416,7 +438,9 @@ class WeakConflictSolver(Rule): if to_append: to_remove.extend(weak_dup_matches) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class CountValidator(Rule): @@ -442,7 +466,9 @@ class CountValidator(Rule): season_count.append(count) else: to_remove.append(count) - return to_remove, episode_count, season_count + if to_remove or episode_count or season_count: + return to_remove, episode_count, season_count + return False class SeePatternRange(Rule): @@ -477,7 +503,9 @@ class SeePatternRange(Rule): to_remove.append(separator) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class AbstractSeparatorRange(Rule): @@ -533,7 +561,9 @@ class AbstractSeparatorRange(Rule): previous_match = next_match - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False class RenameToAbsoluteEpisode(Rule): @@ -629,20 +659,41 @@ class RemoveWeak(Rule): Remove weak-episode matches which appears after video, source, and audio matches. """ priority = 16 - consequence = RemoveMatch + consequence = RemoveMatch, AppendMatch + + def __init__(self, episode_words): + super(RemoveWeak, self).__init__() + self.episode_words = episode_words def when(self, matches, context): to_remove = [] + to_append = [] for filepart in matches.markers.named('path'): weaks = matches.range(filepart.start, filepart.end, predicate=lambda m: 'weak-episode' in m.tags) if weaks: - previous = matches.previous(weaks[0], predicate=lambda m: m.name in ( + weak = weaks[0] + previous = matches.previous(weak, predicate=lambda m: m.name in ( 'audio_codec', 'screen_size', 'streaming_service', 'source', 'video_profile', 'audio_channels', 'audio_profile'), index=0) if previous and not matches.holes( - previous.end, weaks[0].start, predicate=lambda m: m.raw.strip(seps)): + previous.end, weak.start, predicate=lambda m: m.raw.strip(seps)): + if previous.raw.lower() in self.episode_words: + try: + episode = copy.copy(weak) + episode.name = 'episode' + episode.value = int(weak.value) + episode.start = previous.start + episode.private = False + episode.tags = [] + + to_append.append(episode) + except ValueError: + pass + to_remove.extend(weaks) - return to_remove + if to_remove or to_append: + return to_remove, to_append + return False class RemoveWeakIfSxxExx(Rule): @@ -856,4 +907,6 @@ class RenameToDiscMatch(Rule): markers.append(marker) discs.extend(sorted(marker.initiator.children.named('episode'), key=lambda m: m.value)) - return discs, markers, to_remove + if discs or markers or to_remove: + return discs, markers, to_remove + return False diff --git a/libs/common/guessit/rules/properties/language.py b/libs/common/guessit/rules/properties/language.py index bcdbda8b..3f83bc34 100644 --- a/libs/common/guessit/rules/properties/language.py +++ b/libs/common/guessit/rules/properties/language.py @@ -72,6 +72,8 @@ def language(config, common_words): UNDETERMINED = babelfish.Language('und') +MULTIPLE = babelfish.Language('mul') +NON_SPECIFIC_LANGUAGES = frozenset([UNDETERMINED, MULTIPLE]) class GuessitConverter(babelfish.LanguageReverseConverter): # pylint: disable=missing-docstring @@ -388,7 +390,9 @@ class SubtitlePrefixLanguageRule(Rule): to_remove.extend(matches.conflicting(lang)) if prefix in to_remove: to_remove.remove(prefix) - return to_rename, to_remove + if to_rename or to_remove: + return to_rename, to_remove + return False def then(self, matches, when_response, context): to_rename, to_remove = when_response @@ -425,7 +429,9 @@ class SubtitleSuffixLanguageRule(Rule): to_append.append(lang) if suffix in to_remove: to_remove.remove(suffix) - return to_append, to_remove + if to_append or to_remove: + return to_append, to_remove + return False def then(self, matches, when_response, context): to_rename, to_remove = when_response @@ -478,6 +484,7 @@ class RemoveInvalidLanguages(Rule): """Remove language matches that matches the blacklisted common words.""" consequence = RemoveMatch + priority = 32 def __init__(self, common_words): """Constructor.""" diff --git a/libs/common/guessit/rules/properties/other.py b/libs/common/guessit/rules/properties/other.py index 330caa92..c7dc9a88 100644 --- a/libs/common/guessit/rules/properties/other.py +++ b/libs/common/guessit/rules/properties/other.py @@ -11,7 +11,7 @@ from rebulk.remodule import re from ..common import dash from ..common import seps from ..common.pattern import is_disabled -from ..common.validators import seps_after, seps_before, seps_surround, compose +from ..common.validators import seps_after, seps_before, seps_surround, and_ from ...reutils import build_or_pattern from ...rules.common.formatters import raw_cleanup @@ -35,11 +35,16 @@ def other(config): # pylint:disable=unused-argument,too-many-statements rebulk.regex('ws', 'wide-?screen', value='Widescreen') rebulk.regex('Re-?Enc(?:oded)?', value='Reencoded') - rebulk.string('Proper', 'Repack', 'Rerip', value='Proper', + rebulk.string('Repack', 'Rerip', value='Proper', tags=['streaming_service.prefix', 'streaming_service.suffix']) + rebulk.string('Proper', value='Proper', + tags=['has-neighbor', 'streaming_service.prefix', 'streaming_service.suffix']) rebulk.regex('Real-Proper', 'Real-Repack', 'Real-Rerip', value='Proper', tags=['streaming_service.prefix', 'streaming_service.suffix', 'real']) + rebulk.regex('Real', value='Proper', + tags=['has-neighbor', 'streaming_service.prefix', 'streaming_service.suffix', 'real']) + rebulk.string('Fix', 'Fixed', value='Fix', tags=['has-neighbor-before', 'has-neighbor-after', 'streaming_service.prefix', 'streaming_service.suffix']) rebulk.string('Dirfix', 'Nfofix', 'Prooffix', value='Fix', @@ -72,16 +77,18 @@ def other(config): # pylint:disable=unused-argument,too-many-statements private_names=['completeArticle', 'completeWordsBefore', 'completeWordsAfter'], value={'other': 'Complete'}, tags=['release-group-prefix'], - validator={'__parent__': compose(seps_surround, validate_complete)}) + validator={'__parent__': and_(seps_surround, validate_complete)}) rebulk.string('R5', value='Region 5') rebulk.string('RC', value='Region C') rebulk.regex('Pre-?Air', value='Preair') - rebulk.regex('(?:PS-?)?Vita', value='PS Vita') + rebulk.regex('(?:PS-?)Vita', value='PS Vita') + rebulk.regex('Vita', value='PS Vita', tags='has-neighbor') rebulk.regex('(HD)(?PRip)', value={'other': 'HD', 'another': 'Rip'}, private_parent=True, children=True, validator={'__parent__': seps_surround}, validate_all=True) - for value in ('Screener', 'Remux', '3D', 'PAL', 'SECAM', 'NTSC', 'XXX'): + for value in ('Screener', 'Remux', 'PAL', 'SECAM', 'NTSC', 'XXX'): rebulk.string(value, value=value) + rebulk.string('3D', value='3D', tags='has-neighbor') rebulk.string('HQ', value='High Quality', tags='uhdbluray-neighbor') rebulk.string('HR', value='High Resolution') @@ -90,6 +97,7 @@ def other(config): # pylint:disable=unused-argument,too-many-statements rebulk.string('mHD', 'HDLight', value='Micro HD') rebulk.string('LDTV', value='Low Definition') rebulk.string('HFR', value='High Frame Rate') + rebulk.string('VFR', value='Variable Frame Rate') rebulk.string('HD', value='HD', validator=None, tags=['streaming_service.prefix', 'streaming_service.suffix']) rebulk.regex('Full-?HD', 'FHD', value='Full HD', validator=None, @@ -128,13 +136,15 @@ def other(config): # pylint:disable=unused-argument,too-many-statements rebulk.regex('BT-?2020', value='BT.2020', tags='uhdbluray-neighbor') rebulk.string('Sample', value='Sample', tags=['at-end', 'not-a-release-group']) + rebulk.string('Extras', value='Extras', tags='has-neighbor') + rebulk.regex('Digital-?Extras?', value='Extras') rebulk.string('Proof', value='Proof', tags=['at-end', 'not-a-release-group']) rebulk.string('Obfuscated', 'Scrambled', value='Obfuscated', tags=['at-end', 'not-a-release-group']) rebulk.string('xpost', 'postbot', 'asrequested', value='Repost', tags='not-a-release-group') rebulk.rules(RenameAnotherToOther, ValidateHasNeighbor, ValidateHasNeighborAfter, ValidateHasNeighborBefore, ValidateScreenerRule, ValidateMuxRule, ValidateHardcodedSubs, ValidateStreamingServiceNeighbor, - ValidateAtEnd, ProperCountRule) + ValidateAtEnd, ValidateReal, ProperCountRule) return rebulk @@ -354,3 +364,20 @@ class ValidateAtEnd(Rule): to_remove.append(match) return to_remove + + +class ValidateReal(Rule): + """ + Validate Real + """ + consequence = RemoveMatch + priority = 64 + + def when(self, matches, context): + ret = [] + for filepart in matches.markers.named('path'): + for match in matches.range(filepart.start, filepart.end, lambda m: m.name == 'other' and 'real' in m.tags): + if not matches.range(filepart.start, match.start): + ret.append(match) + + return ret diff --git a/libs/common/guessit/rules/properties/part.py b/libs/common/guessit/rules/properties/part.py index ec038b18..c1123394 100644 --- a/libs/common/guessit/rules/properties/part.py +++ b/libs/common/guessit/rules/properties/part.py @@ -8,7 +8,7 @@ from rebulk.remodule import re from rebulk import Rebulk from ..common import dash from ..common.pattern import is_disabled -from ..common.validators import seps_surround, int_coercable, compose +from ..common.validators import seps_surround, int_coercable, and_ from ..common.numeral import numeral, parse_numeral from ...reutils import build_or_pattern @@ -41,6 +41,6 @@ def part(config): # pylint:disable=unused-argument rebulk.regex(build_or_pattern(prefixes) + r'-?(?P' + numeral + r')', prefixes=prefixes, validate_all=True, private_parent=True, children=True, formatter=parse_numeral, - validator={'part': compose(validate_roman, lambda m: 0 < m.value < 100)}) + validator={'part': and_(validate_roman, lambda m: 0 < m.value < 100)}) return rebulk diff --git a/libs/common/guessit/rules/properties/release_group.py b/libs/common/guessit/rules/properties/release_group.py index ff1ac660..ecff808b 100644 --- a/libs/common/guessit/rules/properties/release_group.py +++ b/libs/common/guessit/rules/properties/release_group.py @@ -9,8 +9,8 @@ from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch from rebulk.match import Match from ..common import seps -from ..common.expected import build_expected_function from ..common.comparators import marker_sorted +from ..common.expected import build_expected_function from ..common.formatters import cleanup from ..common.pattern import is_disabled from ..common.validators import int_coercable, seps_surround @@ -50,7 +50,7 @@ def release_group(config): if string.lower().endswith(forbidden) and string[-len(forbidden) - 1:-len(forbidden)] in seps: string = string[:len(forbidden)] string = string.strip(groupname_seps) - return string + return string.strip() rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'release_group')) @@ -72,7 +72,9 @@ _scene_previous_names = ('video_codec', 'source', 'video_api', 'audio_codec', 'a 'audio_channels', 'screen_size', 'other', 'container', 'language', 'subtitle_language', 'subtitle_language.suffix', 'subtitle_language.prefix', 'language.suffix') -_scene_previous_tags = ('release-group-prefix', ) +_scene_previous_tags = ('release-group-prefix',) + +_scene_no_previous_tags = ('no-release-group-prefix',) class DashSeparatedReleaseGroup(Rule): @@ -193,7 +195,8 @@ class DashSeparatedReleaseGroup(Rule): if releasegroup.value: to_append.append(releasegroup) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append class SceneReleaseGroup(Rule): @@ -212,6 +215,17 @@ class SceneReleaseGroup(Rule): super(SceneReleaseGroup, self).__init__() self.value_formatter = value_formatter + @staticmethod + def is_previous_match(match): + """ + Check if match can precede release_group + + :param match: + :return: + """ + return not match.tagged(*_scene_no_previous_tags) if match.name in _scene_previous_names else \ + match.tagged(*_scene_previous_tags) + def when(self, matches, context): # pylint:disable=too-many-locals # If a release_group is found before, ignore this kind of release_group rule. @@ -253,13 +267,12 @@ class SceneReleaseGroup(Rule): if match.start < filepart.start: return False - return not match.private or match.name in _scene_previous_names + return not match.private or self.is_previous_match(match) previous_match = matches.previous(last_hole, previous_match_filter, index=0) - if previous_match and (previous_match.name in _scene_previous_names or - any(tag in previous_match.tags for tag in _scene_previous_tags)) and \ + if previous_match and (self.is_previous_match(previous_match)) and \ not matches.input_string[previous_match.end:last_hole.start].strip(seps) \ and not int_coercable(last_hole.value.strip(seps)): @@ -300,11 +313,11 @@ class AnimeReleaseGroup(Rule): # If a release_group is found before, ignore this kind of release_group rule. if matches.named('release_group'): - return to_remove, to_append + return False if not matches.named('episode') and not matches.named('season') and matches.named('release_group'): # This doesn't seems to be an anime, and we already found another release_group. - return to_remove, to_append + return False for filepart in marker_sorted(matches.markers.named('path'), matches): @@ -328,4 +341,7 @@ class AnimeReleaseGroup(Rule): to_append.append(group) to_remove.extend(matches.range(empty_group.start, empty_group.end, lambda m: 'weak-language' in m.tags)) - return to_remove, to_append + + if to_remove or to_append: + return to_remove, to_append + return False diff --git a/libs/common/guessit/rules/properties/screen_size.py b/libs/common/guessit/rules/properties/screen_size.py index 83a797c1..77d5d052 100644 --- a/libs/common/guessit/rules/properties/screen_size.py +++ b/libs/common/guessit/rules/properties/screen_size.py @@ -24,8 +24,8 @@ def screen_size(config): :return: Created Rebulk object :rtype: Rebulk """ - interlaced = frozenset({res for res in config['interlaced']}) - progressive = frozenset({res for res in config['progressive']}) + interlaced = frozenset(config['interlaced']) + progressive = frozenset(config['progressive']) frame_rates = [re.escape(rate) for rate in config['frame_rates']] min_ar = config['min_ar'] max_ar = config['max_ar'] diff --git a/libs/common/guessit/rules/properties/source.py b/libs/common/guessit/rules/properties/source.py index ae9a7b03..2fe55618 100644 --- a/libs/common/guessit/rules/properties/source.py +++ b/libs/common/guessit/rules/properties/source.py @@ -12,7 +12,7 @@ from rebulk import AppendMatch, Rebulk, RemoveMatch, Rule from .audio_codec import HqConflictRule from ..common import dash, seps from ..common.pattern import is_disabled -from ..common.validators import seps_before, seps_after +from ..common.validators import seps_before, seps_after, or_ def source(config): # pylint:disable=unused-argument @@ -26,7 +26,10 @@ def source(config): # pylint:disable=unused-argument """ rebulk = Rebulk(disabled=lambda context: is_disabled(context, 'source')) rebulk = rebulk.regex_defaults(flags=re.IGNORECASE, abbreviations=[dash], private_parent=True, children=True) - rebulk.defaults(name='source', tags=['video-codec-prefix', 'streaming_service.suffix']) + rebulk = rebulk.defaults(name='source', + tags=['video-codec-prefix', 'streaming_service.suffix'], + validate_all=True, + validator={'__parent__': or_(seps_before, seps_after)}) rip_prefix = '(?PRip)-?' rip_suffix = '-?(?PRip)' @@ -42,7 +45,7 @@ def source(config): # pylint:disable=unused-argument def demote_other(match, other): # pylint: disable=unused-argument """Default conflict solver with 'other' property.""" - return other if other.name == 'other' else '__default__' + return other if other.name == 'other' or other.name == 'release_group' else '__default__' rebulk.regex(*build_source_pattern('VHS', suffix=rip_optional_suffix), value={'source': 'VHS', 'other': 'Rip'}) @@ -92,8 +95,9 @@ def source(config): # pylint:disable=unused-argument # WEBCap is a synonym to WEBRip, mostly used by non english rebulk.regex(*build_source_pattern('WEB-?(?PCap)', suffix=rip_optional_suffix), value={'source': 'Web', 'other': 'Rip', 'another': 'Rip'}) - rebulk.regex(*build_source_pattern('WEB-?DL', 'WEB-?U?HD', 'WEB', 'DL-?WEB', 'DL(?=-?Mux)'), + rebulk.regex(*build_source_pattern('WEB-?DL', 'WEB-?U?HD', 'DL-?WEB', 'DL(?=-?Mux)'), value={'source': 'Web'}) + rebulk.regex('(WEB)', value='Web', tags='weak.source') rebulk.regex(*build_source_pattern('HD-?DVD', suffix=rip_optional_suffix), value={'source': 'HD-DVD', 'other': 'Rip'}) @@ -118,7 +122,7 @@ def source(config): # pylint:disable=unused-argument rebulk.regex(*build_source_pattern('DSR?', 'SAT', suffix=rip_suffix), value={'source': 'Satellite', 'other': 'Rip'}) - rebulk.rules(ValidateSource, UltraHdBlurayRule) + rebulk.rules(ValidateSourcePrefixSuffix, ValidateWeakSource, UltraHdBlurayRule) return rebulk @@ -170,32 +174,62 @@ class UltraHdBlurayRule(Rule): to_remove.append(match) to_append.append(new_source) - return to_remove, to_append + if to_remove or to_append: + return to_remove, to_append + return False -class ValidateSource(Rule): +class ValidateSourcePrefixSuffix(Rule): """ - Validate source with screener property, with video_codec property or separated + Validate source with source prefix, source suffix. """ priority = 64 consequence = RemoveMatch def when(self, matches, context): ret = [] - for match in matches.named('source'): - match = match.initiator - if not seps_before(match) and \ - not matches.range(match.start - 1, match.start - 2, - lambda m: 'source-prefix' in m.tags): - if match.children: - ret.extend(match.children) - ret.append(match) - continue - if not seps_after(match) and \ - not matches.range(match.end, match.end + 1, - lambda m: 'source-suffix' in m.tags): - if match.children: - ret.extend(match.children) - ret.append(match) - continue + for filepart in matches.markers.named('path'): + for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'source'): + match = match.initiator + if not seps_before(match) and \ + not matches.range(match.start - 1, match.start - 2, + lambda m: 'source-prefix' in m.tags): + if match.children: + ret.extend(match.children) + ret.append(match) + continue + if not seps_after(match) and \ + not matches.range(match.end, match.end + 1, + lambda m: 'source-suffix' in m.tags): + if match.children: + ret.extend(match.children) + ret.append(match) + continue + + return ret + + +class ValidateWeakSource(Rule): + """ + Validate weak source + """ + dependency = [ValidateSourcePrefixSuffix] + priority = 64 + consequence = RemoveMatch + + def when(self, matches, context): + ret = [] + for filepart in matches.markers.named('path'): + for match in matches.range(filepart.start, filepart.end, predicate=lambda m: m.name == 'source'): + # if there are more than 1 source in this filepart, just before the year and with holes for the title + # most likely the source is part of the title + if 'weak.source' in match.tags \ + and matches.range(match.end, filepart.end, predicate=lambda m: m.name == 'source') \ + and matches.holes(filepart.start, match.start, + predicate=lambda m: m.value.strip(seps), index=-1): + if match.children: + ret.extend(match.children) + ret.append(match) + continue + return ret diff --git a/libs/common/guessit/rules/properties/streaming_service.py b/libs/common/guessit/rules/properties/streaming_service.py index 1302befb..f467f20a 100644 --- a/libs/common/guessit/rules/properties/streaming_service.py +++ b/libs/common/guessit/rules/properties/streaming_service.py @@ -25,133 +25,13 @@ def streaming_service(config): # pylint: disable=too-many-statements,unused-arg rebulk = rebulk.string_defaults(ignore_case=True).regex_defaults(flags=re.IGNORECASE, abbreviations=[dash]) rebulk.defaults(name='streaming_service', tags=['source-prefix']) - rebulk.string('AE', 'A&E', value='A&E') - rebulk.string('AMBC', value='ABC') - rebulk.string('AUBC', value='ABC Australia') - rebulk.string('AJAZ', value='Al Jazeera English') - rebulk.string('AMC', value='AMC') - rebulk.string('AMZN', 'Amazon', value='Amazon Prime') - rebulk.regex('Amazon-?Prime', value='Amazon Prime') - rebulk.string('AS', value='Adult Swim') - rebulk.regex('Adult-?Swim', value='Adult Swim') - rebulk.string('ATK', value="America's Test Kitchen") - rebulk.string('ANPL', value='Animal Planet') - rebulk.string('ANLB', value='AnimeLab') - rebulk.string('AOL', value='AOL') - rebulk.string('ARD', value='ARD') - rebulk.string('iP', value='BBC iPlayer') - rebulk.regex('BBC-?iPlayer', value='BBC iPlayer') - rebulk.string('BRAV', value='BravoTV') - rebulk.string('CNLP', value='Canal+') - rebulk.string('CN', value='Cartoon Network') - rebulk.string('CBC', value='CBC') - rebulk.string('CBS', value='CBS') - rebulk.string('CNBC', value='CNBC') - rebulk.string('CC', value='Comedy Central') - rebulk.string('4OD', value='Channel 4') - rebulk.string('CHGD', value='CHRGD') - rebulk.string('CMAX', value='Cinemax') - rebulk.string('CMT', value='Country Music Television') - rebulk.regex('Comedy-?Central', value='Comedy Central') - rebulk.string('CCGC', value='Comedians in Cars Getting Coffee') - rebulk.string('CR', value='Crunchy Roll') - rebulk.string('CRKL', value='Crackle') - rebulk.regex('Crunchy-?Roll', value='Crunchy Roll') - rebulk.string('CSPN', value='CSpan') - rebulk.string('CTV', value='CTV') - rebulk.string('CUR', value='CuriosityStream') - rebulk.string('CWS', value='CWSeed') - rebulk.string('DSKI', value='Daisuki') - rebulk.string('DHF', value='Deadhouse Films') - rebulk.string('DDY', value='Digiturk Diledigin Yerde') - rebulk.string('DISC', 'Discovery', value='Discovery') - rebulk.string('DSNY', 'Disney', value='Disney') - rebulk.string('DIY', value='DIY Network') - rebulk.string('DOCC', value='Doc Club') - rebulk.string('DPLY', value='DPlay') - rebulk.string('ETV', value='E!') - rebulk.string('EPIX', value='ePix') - rebulk.string('ETTV', value='El Trece') - rebulk.string('ESPN', value='ESPN') - rebulk.string('ESQ', value='Esquire') - rebulk.string('FAM', value='Family') - rebulk.string('FJR', value='Family Jr') - rebulk.string('FOOD', value='Food Network') - rebulk.string('FOX', value='Fox') - rebulk.string('FREE', value='Freeform') - rebulk.string('FYI', value='FYI Network') - rebulk.string('GLBL', value='Global') - rebulk.string('GLOB', value='GloboSat Play') - rebulk.string('HLMK', value='Hallmark') - rebulk.string('HBO', value='HBO Go') - rebulk.regex('HBO-?Go', value='HBO Go') - rebulk.string('HGTV', value='HGTV') - rebulk.string('HIST', 'History', value='History') - rebulk.string('HULU', value='Hulu') - rebulk.string('ID', value='Investigation Discovery') - rebulk.string('IFC', value='IFC') - rebulk.string('iTunes', 'iT', value='iTunes') - rebulk.string('ITV', value='ITV') - rebulk.string('KNOW', value='Knowledge Network') - rebulk.string('LIFE', value='Lifetime') - rebulk.string('MTOD', value='Motor Trend OnDemand') - rebulk.string('MNBC', value='MSNBC') - rebulk.string('MTV', value='MTV') - rebulk.string('NATG', value='National Geographic') - rebulk.regex('National-?Geographic', value='National Geographic') - rebulk.string('NBA', value='NBA TV') - rebulk.regex('NBA-?TV', value='NBA TV') - rebulk.string('NBC', value='NBC') - rebulk.string('NF', 'Netflix', value='Netflix') - rebulk.string('NFL', value='NFL') - rebulk.string('NFLN', value='NFL Now') - rebulk.string('GC', value='NHL GameCenter') - rebulk.string('NICK', 'Nickelodeon', value='Nickelodeon') - rebulk.string('NRK', value='Norsk Rikskringkasting') - rebulk.string('PBS', value='PBS') - rebulk.string('PBSK', value='PBS Kids') - rebulk.string('PSN', value='Playstation Network') - rebulk.string('PLUZ', value='Pluzz') - rebulk.string('RTE', value='RTE One') - rebulk.string('SBS', value='SBS (AU)') - rebulk.string('SESO', 'SeeSo', value='SeeSo') - rebulk.string('SHMI', value='Shomi') - rebulk.string('SPIK', value='Spike') - rebulk.string('SPKE', value='Spike TV') - rebulk.regex('Spike-?TV', value='Spike TV') - rebulk.string('SNET', value='Sportsnet') - rebulk.string('SPRT', value='Sprout') - rebulk.string('STAN', value='Stan') - rebulk.string('STZ', value='Starz') - rebulk.string('SVT', value='Sveriges Television') - rebulk.string('SWER', value='SwearNet') - rebulk.string('SYFY', value='Syfy') - rebulk.string('TBS', value='TBS') - rebulk.string('TFOU', value='TFou') - rebulk.string('CW', value='The CW') - rebulk.regex('The-?CW', value='The CW') - rebulk.string('TLC', value='TLC') - rebulk.string('TUBI', value='TubiTV') - rebulk.string('TV3', value='TV3 Ireland') - rebulk.string('TV4', value='TV4 Sweeden') - rebulk.string('TVL', value='TV Land') - rebulk.regex('TV-?Land', value='TV Land') - rebulk.string('UFC', value='UFC') - rebulk.string('UKTV', value='UKTV') - rebulk.string('UNIV', value='Univision') - rebulk.string('USAN', value='USA Network') - rebulk.string('VLCT', value='Velocity') - rebulk.string('VH1', value='VH1') - rebulk.string('VICE', value='Viceland') - rebulk.string('VMEO', value='Vimeo') - rebulk.string('VRV', value='VRV') - rebulk.string('WNET', value='W Network') - rebulk.string('WME', value='WatchMe') - rebulk.string('WWEN', value='WWE Network') - rebulk.string('XBOX', value='Xbox Video') - rebulk.string('YHOO', value='Yahoo') - rebulk.string('RED', value='YouTube Red') - rebulk.string('ZDF', value='ZDF') + for value, items in config.items(): + patterns = items if isinstance(items, list) else [items] + for pattern in patterns: + if pattern.startswith('re:'): + rebulk.regex(pattern, value=value) + else: + rebulk.string(pattern, value=value) rebulk.rules(ValidateStreamingService) @@ -161,7 +41,7 @@ def streaming_service(config): # pylint: disable=too-many-statements,unused-arg class ValidateStreamingService(Rule): """Validate streaming service matches.""" - priority = 32 + priority = 128 consequence = RemoveMatch def when(self, matches, context): diff --git a/libs/common/guessit/rules/properties/title.py b/libs/common/guessit/rules/properties/title.py index d1cafe2a..0d263016 100644 --- a/libs/common/guessit/rules/properties/title.py +++ b/libs/common/guessit/rules/properties/title.py @@ -8,7 +8,12 @@ from rebulk import Rebulk, Rule, AppendMatch, RemoveMatch, AppendTags from rebulk.formatters import formatters from .film import FilmTitleRule -from .language import SubtitlePrefixLanguageRule, SubtitleSuffixLanguageRule, SubtitleExtensionRule +from .language import ( + SubtitlePrefixLanguageRule, + SubtitleSuffixLanguageRule, + SubtitleExtensionRule, + NON_SPECIFIC_LANGUAGES +) from ..common import seps, title_seps from ..common.comparators import marker_sorted from ..common.expected import build_expected_function @@ -88,12 +93,19 @@ class TitleBaseRule(Rule): :rtype: """ cropped_holes = [] + group_markers = matches.markers.named('group') + for group_marker in group_markers: + path_marker = matches.markers.at_match(group_marker, predicate=lambda m: m.name == 'path', index=0) + if path_marker and path_marker.span == group_marker.span: + group_markers.remove(group_marker) + for hole in holes: - group_markers = matches.markers.named('group') cropped_holes.extend(hole.crop(group_markers)) + return cropped_holes - def is_ignored(self, match): + @staticmethod + def is_ignored(match): """ Ignore matches when scanning for title (hole). @@ -130,7 +142,8 @@ class TitleBaseRule(Rule): for outside in outside_matches: other_languages.extend(matches.range(outside.start, outside.end, lambda c_match: c_match.name == match.name and - c_match not in to_keep)) + c_match not in to_keep and + c_match.value not in NON_SPECIFIC_LANGUAGES)) if not other_languages and (not starting or len(match.raw) <= 3): return True @@ -239,7 +252,7 @@ class TitleBaseRule(Rule): to_remove = [] if matches.named(self.match_name, lambda match: 'expected' in match.tags): - return ret, to_remove + return False fileparts = [filepart for filepart in list(marker_sorted(matches.markers.named('path'), matches)) if not self.filepart_filter or self.filepart_filter(filepart, matches)] @@ -272,7 +285,9 @@ class TitleBaseRule(Rule): ret.extend(titles) to_remove.extend(to_remove_c) - return ret, to_remove + if ret or to_remove: + return ret, to_remove + return False class TitleFromPosition(TitleBaseRule): @@ -329,4 +344,6 @@ class PreferTitleWithYear(Rule): for title_match in titles: if title_match.value not in title_values: to_remove.append(title_match) - return to_remove, to_tag + if to_remove or to_tag: + return to_remove, to_tag + return False diff --git a/libs/common/guessit/rules/properties/video_codec.py b/libs/common/guessit/rules/properties/video_codec.py index b08ddcae..842a03c7 100644 --- a/libs/common/guessit/rules/properties/video_codec.py +++ b/libs/common/guessit/rules/properties/video_codec.py @@ -3,9 +3,8 @@ """ video_codec and video_profile property """ -from rebulk.remodule import re - from rebulk import Rebulk, Rule, RemoveMatch +from rebulk.remodule import re from ..common import dash from ..common.pattern import is_disabled @@ -43,7 +42,8 @@ def video_codec(config): # pylint:disable=unused-argument # http://blog.mediacoderhq.com/h264-profiles-and-levels/ # https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC - rebulk.defaults(name="video_profile", + rebulk.defaults(clear=True, + name="video_profile", validator=seps_surround, disabled=lambda context: is_disabled(context, 'video_profile')) @@ -66,7 +66,8 @@ def video_codec(config): # pylint:disable=unused-argument rebulk.string('DXVA', value='DXVA', name='video_api', disabled=lambda context: is_disabled(context, 'video_api')) - rebulk.defaults(name='color_depth', + rebulk.defaults(clear=True, + name='color_depth', validator=seps_surround, disabled=lambda context: is_disabled(context, 'color_depth')) rebulk.regex('12.?bits?', value='12-bit') diff --git a/libs/common/guessit/rules/properties/website.py b/libs/common/guessit/rules/properties/website.py index 00dfadd1..c1965311 100644 --- a/libs/common/guessit/rules/properties/website.py +++ b/libs/common/guessit/rules/properties/website.py @@ -67,7 +67,7 @@ def website(config): """ Validator for next website matches """ - return any(name in ['season', 'episode', 'year'] for name in match.names) + return match.named('season', 'episode', 'year') def when(self, matches, context): to_remove = [] @@ -80,7 +80,9 @@ def website(config): if not safe: suffix = matches.next(website_match, PreferTitleOverWebsite.valid_followers, 0) if suffix: - to_remove.append(website_match) + group = matches.markers.at_match(website_match, lambda marker: marker.name == 'group', 0) + if not group: + to_remove.append(website_match) return to_remove rebulk.rules(PreferTitleOverWebsite, ValidateWebsitePrefix) diff --git a/libs/common/guessit/test/enable_disable_properties.yml b/libs/common/guessit/test/enable_disable_properties.yml index 86c659d6..ada9c347 100644 --- a/libs/common/guessit/test/enable_disable_properties.yml +++ b/libs/common/guessit/test/enable_disable_properties.yml @@ -35,9 +35,9 @@ -cd: 1 -cd_count: 3 -? This.Is.Us +? This.is.Us : options: --exclude country - title: This Is Us + title: This is Us -country: US ? 2015.01.31 @@ -286,9 +286,9 @@ : options: --exclude website -website: wawa.co.uk -? movie.mkv +? movie.mp4 : options: --exclude mimetype - -mimetype: video/x-matroska + -mimetype: video/mp4 ? another movie.mkv : options: --exclude container diff --git a/libs/common/guessit/test/episodes.yml b/libs/common/guessit/test/episodes.yml index f7b5c3df..4bbbde4a 100644 --- a/libs/common/guessit/test/episodes.yml +++ b/libs/common/guessit/test/episodes.yml @@ -201,9 +201,9 @@ ? Series/My Name Is Earl/My.Name.Is.Earl.S01Extras.-.Bad.Karma.DVDRip.XviD.avi : title: My Name Is Earl season: 1 - episode_title: Extras - Bad Karma + episode_title: Bad Karma source: DVD - other: Rip + other: [Extras, Rip] video_codec: Xvid ? series/Freaks And Geeks/Season 1/Episode 4 - Kim Kelly Is My Friend-eng(1).srt @@ -1917,9 +1917,11 @@ ? Duck.Dynasty.S02E07.Streik.German.DOKU.DL.WS.DVDRiP.x264-CDP : episode: 7 - episode_title: Streik German + episode_title: Streik source: DVD - language: mul + language: + - German + - Multi other: [Documentary, Widescreen, Rip] release_group: CDP season: 2 @@ -1930,9 +1932,11 @@ ? Family.Guy.S13E14.JOLO.German.AC3D.DL.720p.WebHD.x264-CDD : audio_codec: Dolby Digital episode: 14 - episode_title: JOLO German + episode_title: JOLO source: Web - language: mul + language: + - German + - Multi release_group: CDD screen_size: 720p season: 13 @@ -3025,7 +3029,7 @@ title: Show Name episode: [493, 494, 495, 496, 497, 498, 500, 501, 502, 503, 504, 505, 506, 507] screen_size: 720p - subtitle_language: fr + other: Variable Frame Rate video_codec: H.264 audio_codec: AAC type: episode @@ -4524,4 +4528,166 @@ video_codec: H.264 audio_codec: MP2 release_group: KIDKAT + type: episode + +? Por Trece Razones - Temporada 2 [HDTV 720p][Cap.201][AC3 5.1 Castellano]/Por Trece Razones 2x01 [des202].mkv +: title: Por Trece Razones + season: 2 + source: HDTV + screen_size: 720p + episode: 1 + audio_codec: Dolby Digital + audio_channels: '5.1' + language: Catalan + release_group: des202 + container: mkv + type: episode + +? Cuerpo de Elite - Temporada 1 [HDTV 720p][Cap.113][AC3 5.1 Esp Castellano]\CuerpoDeElite720p_113_desca202.mkv +: title: Cuerpo de Elite + season: 1 + source: HDTV + screen_size: 720p + episode: 13 + audio_codec: Dolby Digital + audio_channels: '5.1' + language: + - Spanish + - Catalan + container: mkv + type: episode + +? Show.Name.S01E01.St.Patricks.Day.1080p.mkv +: title: Show Name + season: 1 + episode: 1 + episode_title: St Patricks Day + screen_size: 1080p + container: mkv + type: episode + +? Show.Name.S01E01.St.Patricks.Day.1080p-grp.mkv +: title: Show Name + season: 1 + episode: 1 + episode_title: St Patricks Day + screen_size: 1080p + release_group: grp + container: mkv + type: episode + +? Titans.2018.S01E09.Hank.And.Dawn.720p.DCU.WEB-DL.AAC2.0.H264-NTb +: title: Titans + year: 2018 + season: 1 + episode: 9 + episode_title: Hank And Dawn + screen_size: 720p + streaming_service: DC Universe + source: Web + audio_codec: AAC + audio_channels: '2.0' + video_codec: H.264 + release_group: NTb + type: episode + +? S.W.A.T.2017.S01E21.Treibjagd.German.Dubbed.DL.AmazonHD.x264-TVS +: title: S.W.A.T. + year: 2017 + season: 1 + episode: 21 + episode_title: Treibjagd + language: + - German + - Multi + streaming_service: Amazon Prime + other: HD + video_codec: H.264 + release_group: TVS + type: episode + +? S.W.A.T.2017.S01E16.READNFO.720p.HDTV.x264-KILLERS +: title: S.W.A.T. + year: 2017 + season: 1 + episode: 16 + other: Read NFO + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: KILLERS + type: episode + +? /mnt/NAS/NoSubsTVShows/Babylon 5/Season 01/Ep. 02 - Soul Hunter +: title: Babylon 5 + season: 1 + episode: 2 + episode_title: Soul Hunter + type: episode + +? This.is.Us.S01E01.HDTV.x264-KILLERS.mkv +: title: This is Us + season: 1 + episode: 1 + source: HDTV + video_codec: H.264 + release_group: KILLERS + container: mkv + type: episode + +? Videos/Office1080/The Office (US) (2005) Season 2 S02 + Extras (1080p AMZN WEB-DL x265 HEVC 10bit AAC 2.0 LION)/The Office (US) (2005) - S02E12 - The Injury (1080p AMZN WEB-DL x265 LION).mkv +: title: The Office + country: US + year: 2005 + season: 2 + other: Extras + screen_size: 1080p + streaming_service: Amazon Prime + source: Web + video_codec: H.265 + video_profile: High Efficiency Video Coding + color_depth: 10-bit + audio_codec: AAC + audio_channels: '2.0' + release_group: LION + episode: 12 + episode_title: The Injury + container: mkv + type: episode + +? Thumping.Spike.2.E01.DF.WEBRip.720p-DRAMATV.mp4 +: title: Thumping Spike 2 + episode: 1 + source: Web + other: Rip + screen_size: 720p + streaming_service: DramaFever + release_group: DRAMATV + container: mp4 + mimetype: video/mp4 + type: episode + +? About.Time.E01.1080p.VIKI.WEB-DL-BLUEBERRY.mp4 +: title: About Time + episode: 1 + screen_size: 1080p + streaming_service: Viki + source: Web + release_group: BLUEBERRY + container: mp4 + mimetype: video/mp4 + type: episode + +? Eyes.Of.Dawn.1991.E01.480p.MBCVOD.AAC.x264-NOGPR.mp4 +: title: Eyes Of Dawn + year: 1991 + season: 1991 + episode: 1 + screen_size: 480p + streaming_service: MBC + audio_codec: AAC + video_codec: H.264 + release_group: NOGPR + container: mp4 + mimetype: video/mp4 type: episode \ No newline at end of file diff --git a/libs/common/guessit/test/movies.yml b/libs/common/guessit/test/movies.yml index 642012a9..a534ca0f 100644 --- a/libs/common/guessit/test/movies.yml +++ b/libs/common/guessit/test/movies.yml @@ -815,10 +815,12 @@ ? Das.Appartement.German.AC3D.DL.720p.BluRay.x264-TVP : audio_codec: Dolby Digital source: Blu-ray - language: mul + language: + - German + - Multi release_group: TVP screen_size: 720p - title: Das Appartement German + title: Das Appartement type: movie video_codec: H.264 @@ -1723,7 +1725,7 @@ ? Ant-Man.and.the.Wasp.2018.Digital.Extras.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv : title: Ant-Man and the Wasp year: 2018 - alternative_title: Digital Extras + other: Extras screen_size: 1080p streaming_service: Amazon Prime source: Web @@ -1770,4 +1772,15 @@ audio_channels: '5.1' video_codec: H.264 release_group: CMRG - type: movie \ No newline at end of file + type: movie + +? The.Girl.in.the.Spiders.Web.2019.1080p.WEB-DL.x264.AC3-EVO.mkv +: title: The Girl in the Spiders Web + year: 2019 + screen_size: 1080p + source: Web + video_codec: H.264 + audio_codec: Dolby Digital + release_group: EVO + container: mkv + type: movie diff --git a/libs/common/guessit/test/rules/common_words.yml b/libs/common/guessit/test/rules/common_words.yml new file mode 100644 index 00000000..d403a457 --- /dev/null +++ b/libs/common/guessit/test/rules/common_words.yml @@ -0,0 +1,467 @@ +? is +: title: is + +? it +: title: it + +? am +: title: am + +? mad +: title: mad + +? men +: title: men + +? man +: title: man + +? run +: title: run + +? sin +: title: sin + +? st +: title: st + +? to +: title: to + +? 'no' +: title: 'no' + +? non +: title: non + +? war +: title: war + +? min +: title: min + +? new +: title: new + +? car +: title: car + +? day +: title: day + +? bad +: title: bad + +? bat +: title: bat + +? fan +: title: fan + +? fry +: title: fry + +? cop +: title: cop + +? zen +: title: zen + +? gay +: title: gay + +? fat +: title: fat + +? one +: title: one + +? cherokee +: title: cherokee + +? got +: title: got + +? an +: title: an + +? as +: title: as + +? cat +: title: cat + +? her +: title: her + +? be +: title: be + +? hat +: title: hat + +? sun +: title: sun + +? may +: title: may + +? my +: title: my + +? mr +: title: mr + +? rum +: title: rum + +? pi +: title: pi + +? bb +: title: bb + +? bt +: title: bt + +? tv +: title: tv + +? aw +: title: aw + +? by +: title: by + +? md +: other: Mic Dubbed + +? mp +: title: mp + +? cd +: title: cd + +? in +: title: in + +? ad +: title: ad + +? ice +: title: ice + +? ay +: title: ay + +? at +: title: at + +? star +: title: star + +? so +: title: so + +? he +: title: he + +? do +: title: do + +? ax +: title: ax + +? mx +: title: mx + +? bas +: title: bas + +? de +: title: de + +? le +: title: le + +? son +: title: son + +? ne +: title: ne + +? ca +: title: ca + +? ce +: title: ce + +? et +: title: et + +? que +: title: que + +? mal +: title: mal + +? est +: title: est + +? vol +: title: vol + +? or +: title: or + +? mon +: title: mon + +? se +: title: se + +? je +: title: je + +? tu +: title: tu + +? me +: title: me + +? ma +: title: ma + +? va +: title: va + +? au +: country: AU + +? lu +: title: lu + +? wa +: title: wa + +? ga +: title: ga + +? ao +: title: ao + +? la +: title: la + +? el +: title: el + +? del +: title: del + +? por +: title: por + +? mar +: title: mar + +? al +: title: al + +? un +: title: un + +? ind +: title: ind + +? arw +: title: arw + +? ts +: source: Telesync + +? ii +: title: ii + +? bin +: title: bin + +? chan +: title: chan + +? ss +: title: ss + +? san +: title: san + +? oss +: title: oss + +? iii +: title: iii + +? vi +: title: vi + +? ben +: title: ben + +? da +: title: da + +? lt +: title: lt + +? ch +: title: ch + +? sr +: title: sr + +? ps +: title: ps + +? cx +: title: cx + +? vo +: title: vo + +? mkv +: container: mkv + +? avi +: container: avi + +? dmd +: title: dmd + +? the +: title: the + +? dis +: title: dis + +? cut +: title: cut + +? stv +: title: stv + +? des +: title: des + +? dia +: title: dia + +? and +: title: and + +? cab +: title: cab + +? sub +: title: sub + +? mia +: title: mia + +? rim +: title: rim + +? las +: title: las + +? une +: title: une + +? par +: title: par + +? srt +: container: srt + +? ano +: title: ano + +? toy +: title: toy + +? job +: title: job + +? gag +: title: gag + +? reel +: title: reel + +? www +: title: www + +? for +: title: for + +? ayu +: title: ayu + +? csi +: title: csi + +? ren +: title: ren + +? moi +: title: moi + +? sur +: title: sur + +? fer +: title: fer + +? fun +: title: fun + +? two +: title: two + +? big +: title: big + +? psy +: title: psy + +? air +: title: air + +? brazil +: title: brazil + +? jordan +: title: jordan + +? bs +: title: bs + +? kz +: title: kz + +? gt +: title: gt + +? im +: title: im + +? pt +: language: pt + +? scr +: title: scr + +? sd +: title: sd + +? hr +: other: High Resolution diff --git a/libs/common/guessit/test/rules/country.yml b/libs/common/guessit/test/rules/country.yml index 76383180..b3d4d8f1 100644 --- a/libs/common/guessit/test/rules/country.yml +++ b/libs/common/guessit/test/rules/country.yml @@ -5,8 +5,8 @@ : country: US title: this is title -? This.is.us.title -: title: This is us title +? This.is.Us +: title: This is Us ? This.Is.Us : options: --no-default-config diff --git a/libs/common/guessit/test/rules/other.yml b/libs/common/guessit/test/rules/other.yml index e2bea6e7..447f1787 100644 --- a/libs/common/guessit/test/rules/other.yml +++ b/libs/common/guessit/test/rules/other.yml @@ -48,7 +48,7 @@ proper_count: 3 -? Proper +? Proper.720p ? +Repack ? +Rerip : other: Proper @@ -80,7 +80,7 @@ ? Remux : other: Remux -? 3D +? 3D.2019 : other: 3D ? HD diff --git a/libs/common/guessit/test/suggested.json b/libs/common/guessit/test/suggested.json new file mode 100644 index 00000000..dc838ad0 --- /dev/null +++ b/libs/common/guessit/test/suggested.json @@ -0,0 +1,21 @@ +{ + "titles": [ + "13 Reasons Why", + "Star Wars: Episode VII - The Force Awakens", + "3%", + "The 100", + "3 Percent", + "This is Us", + "Open Season 2", + "Game of Thrones", + "The X-Files", + "11.22.63" + ], + "suggested": [ + "13 Reasons Why", + "Star Wars: Episode VII - The Force Awakens", + "The 100", + "Open Season 2", + "11.22.63" + ] +} \ No newline at end of file diff --git a/libs/common/guessit/test/test_api.py b/libs/common/guessit/test/test_api.py index 9abb84d9..391dbced 100644 --- a/libs/common/guessit/test/test_api.py +++ b/libs/common/guessit/test/test_api.py @@ -1,13 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # pylint: disable=no-self-use, pointless-statement, missing-docstring, invalid-name, pointless-string-statement - +import json import os +import sys import pytest import six -from ..api import guessit, properties, GuessitException +from ..api import guessit, properties, suggested_expected, GuessitException __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) @@ -27,12 +28,16 @@ def test_forced_binary(): assert ret and 'title' in ret and isinstance(ret['title'], six.binary_type) -@pytest.mark.skipif('sys.version_info < (3, 4)', reason="Path is not available") +@pytest.mark.skipif(sys.version_info < (3, 4), reason="Path is not available") def test_pathlike_object(): - from pathlib import Path - path = Path('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') - ret = guessit(path) - assert ret and 'title' in ret + try: + from pathlib import Path + + path = Path('Fear.and.Loathing.in.Las.Vegas.FRENCH.ENGLISH.720p.HDDVD.DTS.x264-ESiR.mkv') + ret = guessit(path) + assert ret and 'title' in ret + except ImportError: # pragma: no-cover + pass def test_unicode_japanese(): @@ -69,3 +74,10 @@ def test_exception(): assert "An internal error has occured in guessit" in str(excinfo.value) assert "Guessit Exception Report" in str(excinfo.value) assert "Please report at https://github.com/guessit-io/guessit/issues" in str(excinfo.value) + + +def test_suggested_expected(): + with open(os.path.join(__location__, 'suggested.json'), 'r') as f: + content = json.load(f) + actual = suggested_expected(content['titles']) + assert actual == content['suggested'] diff --git a/libs/common/guessit/test/test_yml.py b/libs/common/guessit/test/test_yml.py index 4f58a056..040796de 100644 --- a/libs/common/guessit/test/test_yml.py +++ b/libs/common/guessit/test/test_yml.py @@ -7,9 +7,8 @@ import os from io import open # pylint: disable=redefined-builtin import babelfish -import pytest -import six -import yaml +import six # pylint:disable=wrong-import-order +import yaml # pylint:disable=wrong-import-order from rebulk.remodule import re from rebulk.utils import is_iterable @@ -21,13 +20,6 @@ logger = logging.getLogger(__name__) __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) -filename_predicate = None -string_predicate = None - - -# filename_predicate = lambda filename: 'episode_title' in filename -# string_predicate = lambda string: '-DVD.BlablaBla.Fix.Blablabla.XVID' in string - class EntryResult(object): def __init__(self, string, negates=False): @@ -134,7 +126,49 @@ class TestYml(object): options_re = re.compile(r'^([ +-]+)(.*)') - files, ids = files_and_ids(filename_predicate) + def _get_unique_id(self, collection, base_id): + ret = base_id + i = 2 + while ret in collection: + suffix = "-" + str(i) + ret = base_id + suffix + i += 1 + return ret + + def pytest_generate_tests(self, metafunc): + if 'yml_test_case' in metafunc.fixturenames: + entries = [] + entry_ids = [] + entry_set = set() + + for filename, _ in zip(*files_and_ids()): + with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: + data = yaml.load(infile, OrderedDictYAMLLoader) + + last_expected = None + for string, expected in reversed(list(data.items())): + if expected is None: + data[string] = last_expected + else: + last_expected = expected + + default = None + try: + default = data['__default__'] + del data['__default__'] + except KeyError: + pass + + for string, expected in data.items(): + TestYml.set_default(expected, default) + string = TestYml.fix_encoding(string, expected) + + entries.append((filename, string, expected)) + unique_id = self._get_unique_id(entry_set, '[' + filename + '] ' + str(string)) + entry_set.add(unique_id) + entry_ids.append(unique_id) + + metafunc.parametrize('yml_test_case', entries, ids=entry_ids) @staticmethod def set_default(expected, default): @@ -143,34 +177,8 @@ class TestYml(object): if k not in expected: expected[k] = v - @pytest.mark.parametrize('filename', files, ids=ids) - def test(self, filename, caplog): - caplog.set_level(logging.INFO) - with open(os.path.join(__location__, filename), 'r', encoding='utf-8') as infile: - data = yaml.load(infile, OrderedDictYAMLLoader) - entries = Results() - - last_expected = None - for string, expected in reversed(list(data.items())): - if expected is None: - data[string] = last_expected - else: - last_expected = expected - - default = None - try: - default = data['__default__'] - del data['__default__'] - except KeyError: - pass - - for string, expected in data.items(): - TestYml.set_default(expected, default) - entry = self.check_data(filename, string, expected) - entries.append(entry) - entries.assert_ok() - - def check_data(self, filename, string, expected): + @classmethod + def fix_encoding(cls, string, expected): if six.PY2: if isinstance(string, six.text_type): string = string.encode('utf-8') @@ -183,16 +191,23 @@ class TestYml(object): expected[k] = v if not isinstance(string, str): string = str(string) - if not string_predicate or string_predicate(string): # pylint: disable=not-callable - entry = self.check(string, expected) - if entry.ok: - logger.debug('[%s] %s', filename, entry) - elif entry.warning: - logger.warning('[%s] %s', filename, entry) - elif entry.error: - logger.error('[%s] %s', filename, entry) - for line in entry.details: - logger.error('[%s] %s', filename, ' ' * 4 + line) + return string + + def test_entry(self, yml_test_case): + filename, string, expected = yml_test_case + result = self.check_data(filename, string, expected) + assert not result.error + + def check_data(self, filename, string, expected): + entry = self.check(string, expected) + if entry.ok: + logger.debug('[%s] %s', filename, entry) + elif entry.warning: + logger.warning('[%s] %s', filename, entry) + elif entry.error: + logger.error('[%s] %s', filename, entry) + for line in entry.details: + logger.error('[%s] %s', filename, ' ' * 4 + line) return entry def check(self, string, expected): diff --git a/libs/common/guessit/test/various.yml b/libs/common/guessit/test/various.yml index 5e689e0b..6fb58deb 100644 --- a/libs/common/guessit/test/various.yml +++ b/libs/common/guessit/test/various.yml @@ -946,3 +946,254 @@ source: Blu-ray audio_codec: DTS-HD type: movie + +? Mr Robot - S03E01 - eps3 0 power-saver-mode h (1080p AMZN WEB-DL x265 HEVC 10bit EAC3 6.0 RCVR).mkv +: title: Mr Robot + season: 3 + episode: 1 + episode_title: eps3 0 power-saver-mode h + screen_size: 1080p + streaming_service: Amazon Prime + source: Web + video_codec: H.265 + video_profile: High Efficiency Video Coding + color_depth: 10-bit + audio_codec: Dolby Digital Plus + audio_channels: '5.1' + release_group: RCVR + container: mkv + type: episode + +? Panorama.15-05-2018.Web-DL.540p.H264.AAC.Subs.mp4 +: title: Panorama + date: 2018-05-15 + source: Web + screen_size: 540p + video_codec: H.264 + audio_codec: AAC + subtitle_language: und + container: mp4 + type: episode + +? Shaolin 2011.720p.BluRay.x264-x0r.mkv +: title: Shaolin + year: 2011 + screen_size: 720p + source: Blu-ray + video_codec: H.264 + release_group: x0r + container: mkv + type: movie + +? '[ Engineering Catastrophes S02E10 1080p AMZN WEB-DL DD+ 2.0 x264-TrollHD ]' +: title: Engineering Catastrophes + season: 2 + episode: 10 + screen_size: 1080p + streaming_service: Amazon Prime + source: Web + audio_codec: Dolby Digital Plus + audio_channels: '2.0' + video_codec: H.264 + release_group: TrollHD + type: episode + +? A Very Harold & Kumar 3D Christmas (2011).mkv +: title: A Very Harold & Kumar 3D Christmas + year: 2011 + container: mkv + type: movie + +? Cleveland.Hustles.S01E03.Downward.Dogs.and.Proper.Pigs.720p.HDTV.x264-W4F +: title: Cleveland Hustles + season: 1 + episode: 3 + episode_title: Downward Dogs and Proper Pigs + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: W4F + type: episode + +? Pawn.Stars.S12E20.The.Pawn.Awakens.REAL.READ.NFO.720p.HDTV.x264-DHD +: title: Pawn Stars + season: 12 + episode: 20 + episode_title: The Pawn Awakens + other: + - Proper + - Read NFO + proper_count: 2 + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: DHD + type: episode + +? Pawn.Stars.S12E22.Racing.Revolution.REAL.720p.HDTV.x264-DHD +: title: Pawn Stars + season: 12 + episode: 22 + episode_title: Racing Revolution + other: Proper + proper_count: 2 + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: DHD + type: episode + +? Luksusfellen.S18E02.REAL.NORWEGiAN.720p.WEB.h264-NORPiLT +: title: Luksusfellen + season: 18 + episode: 2 + other: Proper + proper_count: 2 + language: Norwegian + screen_size: 720p + source: Web + video_codec: H.264 + release_group: NORPiLT + type: episode + +? The.Exorcist.S02E07.REAL.FRENCH.720p.HDTV.x264-SH0W +: title: The Exorcist + season: 2 + episode: 7 + other: Proper + proper_count: 2 + language: fr + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: SH0W + type: episode + +? Outrageous.Acts.of.Science.S05E02.Is.This.for.Real.720p.HDTV.x264-DHD +: title: Outrageous Acts of Science + season: 5 + episode: 2 +# corner case +# episode_title: Is This for Real + screen_size: 720p + source: HDTV + video_codec: H.264 + release_group: DHD + type: episode + +? How.the.Universe.Works.S06E08.Strange.Lives.of.Dwarf.Planets.REAL.720p.WEB.x264-DHD +: title: How the Universe Works + season: 6 + episode: 8 + episode_title: Strange Lives of Dwarf Planets + other: Proper + proper_count: 2 + screen_size: 720p + source: Web + video_codec: H.264 + release_group: DHD + type: episode + +? Vampirina.S01E16.REAL.HDTV.x264-W4F +: title: Vampirina + season: 1 + episode: 16 + other: Proper + proper_count: 2 + source: HDTV + video_codec: H.264 + release_group: W4F + type: episode + +? Test.S01E16.Some Real Episode Title.HDTV.x264-W4F +: title: Test + season: 1 + episode: 16 + episode_title: Some Real Episode Title + source: HDTV + video_codec: H.264 + release_group: W4F + type: episode + +? NOS4A2.S01E01.The.Shorter.Way.REPACK.720p.AMZN.WEB-DL.DDP5.1.H.264-NTG.mkv +: title: NOS4A2 + season: 1 + episode: 1 + episode_title: The Shorter Way + other: Proper + proper_count: 1 + screen_size: 720p + streaming_service: Amazon Prime + source: Web + audio_codec: Dolby Digital Plus + audio_channels: '5.1' + video_codec: H.264 + release_group: NTG + container: mkv + type: episode + +? Star Trek DS9 Ep 2x03 The Siege (Part III) +: title: Star Trek DS9 + season: 2 + episode: 3 + episode_title: The Siege + part: 3 + type: episode + +? The.Red.Line.S01E01 +: title: The Red Line + season: 1 + episode: 1 + type: episode + +? Show.S01E01.WEB.x264-METCON.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: METCON + container: mkv + type: episode + +? Show.S01E01.WEB.x264-TCMEON.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: TCMEON + container: mkv + type: episode + +? Show.S01E01.WEB.x264-MEONTC.mkv +: title: Show + season: 1 + episode: 1 + source: Web + video_codec: H.264 + release_group: MEONTC + container: mkv + type: episode + +? '[TorrentCouch.com].Westworld.S02.Complete.720p.WEB-DL.x264.[MP4].[5.3GB].[Season.2.Full]/[TorrentCouch.com].Westworld.S02E03.720p.WEB-DL.x264.mp4' +: website: TorrentCouch.com + title: Westworld + season: 2 + other: Complete + screen_size: 720p + source: Web + video_codec: H.264 + container: mp4 + size: 5.3GB + episode: 3 + type: episode + +? Vita.&.Virginia.2018.720p.H.264.YTS.LT.mp4 +: title: Vita & Virginia + year: 2018 + screen_size: 720p + video_codec: H.264 + release_group: YTS.LT + container: mp4 + type: movie \ No newline at end of file diff --git a/libs/common/guessit/yamlutils.py b/libs/common/guessit/yamlutils.py index 01ac7778..d04be641 100644 --- a/libs/common/guessit/yamlutils.py +++ b/libs/common/guessit/yamlutils.py @@ -10,19 +10,19 @@ except ImportError: # pragma: no-cover from ordereddict import OrderedDict # pylint:disable=import-error import babelfish -import yaml +import yaml # pylint:disable=wrong-import-order from .rules.common.quantity import BitRate, FrameRate, Size -class OrderedDictYAMLLoader(yaml.Loader): +class OrderedDictYAMLLoader(yaml.SafeLoader): """ A YAML loader that loads mappings into ordered dictionaries. From https://gist.github.com/enaeseth/844388 """ def __init__(self, *args, **kwargs): - yaml.Loader.__init__(self, *args, **kwargs) + yaml.SafeLoader.__init__(self, *args, **kwargs) self.add_constructor(u'tag:yaml.org,2002:map', type(self).construct_yaml_map) self.add_constructor(u'tag:yaml.org,2002:omap', type(self).construct_yaml_map) @@ -58,7 +58,7 @@ class CustomDumper(yaml.SafeDumper): """ Custom YAML Dumper. """ - pass + pass # pylint:disable=unnecessary-pass def default_representer(dumper, data): diff --git a/libs/common/rebulk/__version__.py b/libs/common/rebulk/__version__.py index 1f96b77a..939c554c 100644 --- a/libs/common/rebulk/__version__.py +++ b/libs/common/rebulk/__version__.py @@ -4,4 +4,4 @@ Version module """ # pragma: no cover -__version__ = '1.0.0' +__version__ = '2.0.1' diff --git a/libs/common/rebulk/builder.py b/libs/common/rebulk/builder.py new file mode 100644 index 00000000..c91420aa --- /dev/null +++ b/libs/common/rebulk/builder.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Base builder class for Rebulk +""" +from abc import ABCMeta, abstractmethod +from copy import deepcopy +from logging import getLogger + +from six import add_metaclass + +from .loose import set_defaults +from .pattern import RePattern, StringPattern, FunctionalPattern + +log = getLogger(__name__).log + + +@add_metaclass(ABCMeta) +class Builder(object): + """ + Base builder class for patterns + """ + + def __init__(self): + self._defaults = {} + self._regex_defaults = {} + self._string_defaults = {} + self._functional_defaults = {} + self._chain_defaults = {} + + def reset(self): + """ + Reset all defaults. + + :return: + """ + self.__init__() + + def defaults(self, **kwargs): + """ + Define default keyword arguments for all patterns + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(kwargs, self._defaults, override=True) + return self + + def regex_defaults(self, **kwargs): + """ + Define default keyword arguments for functional patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(kwargs, self._regex_defaults, override=True) + return self + + def string_defaults(self, **kwargs): + """ + Define default keyword arguments for string patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(kwargs, self._string_defaults, override=True) + return self + + def functional_defaults(self, **kwargs): + """ + Define default keyword arguments for functional patterns. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(kwargs, self._functional_defaults, override=True) + return self + + def chain_defaults(self, **kwargs): + """ + Define default keyword arguments for patterns chain. + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(kwargs, self._chain_defaults, override=True) + return self + + def build_re(self, *pattern, **kwargs): + """ + Builds a new regular expression pattern + + :param pattern: + :type pattern: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(self._regex_defaults, kwargs) + set_defaults(self._defaults, kwargs) + return RePattern(*pattern, **kwargs) + + def build_string(self, *pattern, **kwargs): + """ + Builds a new string pattern + + :param pattern: + :type pattern: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(self._string_defaults, kwargs) + set_defaults(self._defaults, kwargs) + return StringPattern(*pattern, **kwargs) + + def build_functional(self, *pattern, **kwargs): + """ + Builds a new functional pattern + + :param pattern: + :type pattern: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + set_defaults(self._functional_defaults, kwargs) + set_defaults(self._defaults, kwargs) + return FunctionalPattern(*pattern, **kwargs) + + def build_chain(self, **kwargs): + """ + Builds a new patterns chain + + :param pattern: + :type pattern: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + from .chain import Chain + set_defaults(self._chain_defaults, kwargs) + set_defaults(self._defaults, kwargs) + chain = Chain(self, **kwargs) + chain._defaults = deepcopy(self._defaults) # pylint: disable=protected-access + chain._regex_defaults = deepcopy(self._regex_defaults) # pylint: disable=protected-access + chain._functional_defaults = deepcopy(self._functional_defaults) # pylint: disable=protected-access + chain._string_defaults = deepcopy(self._string_defaults) # pylint: disable=protected-access + chain._chain_defaults = deepcopy(self._chain_defaults) # pylint: disable=protected-access + return chain + + @abstractmethod + def pattern(self, *pattern): + """ + Register a list of Pattern instance + :param pattern: + :return: + """ + pass + + def regex(self, *pattern, **kwargs): + """ + Add re pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + return self.pattern(self.build_re(*pattern, **kwargs)) + + def string(self, *pattern, **kwargs): + """ + Add string pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + return self.pattern(self.build_string(*pattern, **kwargs)) + + def functional(self, *pattern, **kwargs): + """ + Add functional pattern + + :param pattern: + :type pattern: + :return: self + :rtype: Rebulk + """ + functional = self.build_functional(*pattern, **kwargs) + return self.pattern(functional) + + def chain(self, **kwargs): + """ + Add patterns chain, using configuration of this rebulk + + :param pattern: + :type pattern: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + chain = self.build_chain(**kwargs) + self.pattern(chain) + return chain diff --git a/libs/common/rebulk/chain.py b/libs/common/rebulk/chain.py index dfb6ea44..ba31ec9a 100644 --- a/libs/common/rebulk/chain.py +++ b/libs/common/rebulk/chain.py @@ -6,9 +6,10 @@ Chain patterns and handle repetiting capture group # pylint: disable=super-init-not-called import itertools -from .loose import call, set_defaults +from .builder import Builder +from .loose import call from .match import Match, Matches -from .pattern import Pattern, filter_match_kwargs +from .pattern import Pattern, filter_match_kwargs, BasePattern from .remodule import re @@ -19,150 +20,46 @@ class _InvalidChainException(Exception): pass -class Chain(Pattern): +class Chain(Pattern, Builder): """ Definition of a pattern chain to search for. """ - def __init__(self, rebulk, chain_breaker=None, **kwargs): - call(super(Chain, self).__init__, **kwargs) + def __init__(self, parent, chain_breaker=None, **kwargs): + Builder.__init__(self) + call(Pattern.__init__, self, **kwargs) self._kwargs = kwargs self._match_kwargs = filter_match_kwargs(kwargs) - self._defaults = {} - self._regex_defaults = {} - self._string_defaults = {} - self._functional_defaults = {} if callable(chain_breaker): self.chain_breaker = chain_breaker else: self.chain_breaker = None - self.rebulk = rebulk + self.parent = parent self.parts = [] - def defaults(self, **kwargs): + def pattern(self, *pattern): """ - Define default keyword arguments for all patterns - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._defaults = kwargs - return self - - def regex_defaults(self, **kwargs): - """ - Define default keyword arguments for functional patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._regex_defaults = kwargs - return self - - def string_defaults(self, **kwargs): - """ - Define default keyword arguments for string patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._string_defaults = kwargs - return self - - def functional_defaults(self, **kwargs): - """ - Define default keyword arguments for functional patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._functional_defaults = kwargs - return self - - def chain(self): - """ - Add patterns chain, using configuration from this chain - - :return: - :rtype: - """ - # pylint: disable=protected-access - chain = self.rebulk.chain(**self._kwargs) - chain._defaults = dict(self._defaults) - chain._regex_defaults = dict(self._regex_defaults) - chain._functional_defaults = dict(self._functional_defaults) - chain._string_defaults = dict(self._string_defaults) - return chain - - def regex(self, *pattern, **kwargs): - """ - Add re pattern :param pattern: - :type pattern: - :param kwargs: - :type kwargs: :return: - :rtype: """ - set_defaults(self._kwargs, kwargs) - set_defaults(self._regex_defaults, kwargs) - set_defaults(self._defaults, kwargs) - pattern = self.rebulk.build_re(*pattern, **kwargs) - part = ChainPart(self, pattern) - self.parts.append(part) - return part - - def functional(self, *pattern, **kwargs): - """ - Add functional pattern - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._kwargs, kwargs) - set_defaults(self._functional_defaults, kwargs) - set_defaults(self._defaults, kwargs) - pattern = self.rebulk.build_functional(*pattern, **kwargs) - part = ChainPart(self, pattern) - self.parts.append(part) - return part - - def string(self, *pattern, **kwargs): - """ - Add string pattern - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._kwargs, kwargs) - set_defaults(self._functional_defaults, kwargs) - set_defaults(self._defaults, kwargs) - pattern = self.rebulk.build_string(*pattern, **kwargs) - part = ChainPart(self, pattern) + if not pattern: + raise ValueError("One pattern should be given to the chain") + if len(pattern) > 1: + raise ValueError("Only one pattern can be given to the chain") + part = ChainPart(self, pattern[0]) self.parts.append(part) return part def close(self): """ - Close chain builder to continue registering other pattern - - :return: - :rtype: + Deeply close the chain + :return: Rebulk instance """ - return self.rebulk + parent = self.parent + while isinstance(parent, Chain): + parent = parent.parent + return parent def _match(self, pattern, input_string, context=None): # pylint: disable=too-many-locals,too-many-nested-blocks @@ -173,42 +70,20 @@ class Chain(Pattern): chain_found = False current_chain_matches = [] valid_chain = True - is_chain_start = True for chain_part in self.parts: try: - chain_part_matches, raw_chain_part_matches = Chain._match_chain_part(is_chain_start, chain_part, - chain_input_string, - context) - - Chain._fix_matches_offset(chain_part_matches, input_string, offset) - Chain._fix_matches_offset(raw_chain_part_matches, input_string, offset) - - if raw_chain_part_matches: - grouped_matches_dict = dict() - for match_index, match in itertools.groupby(chain_part_matches, - lambda m: m.match_index): - grouped_matches_dict[match_index] = list(match) - - grouped_raw_matches_dict = dict() - for match_index, raw_match in itertools.groupby(raw_chain_part_matches, - lambda m: m.match_index): - grouped_raw_matches_dict[match_index] = list(raw_match) - - for match_index, grouped_raw_matches in grouped_raw_matches_dict.items(): - chain_found = True - offset = grouped_raw_matches[-1].raw_end - chain_input_string = input_string[offset:] - if not chain_part.is_hidden: - grouped_matches = grouped_matches_dict.get(match_index, []) - if self._chain_breaker_eval(current_chain_matches + grouped_matches): - current_chain_matches.extend(grouped_matches) + chain_part_matches, raw_chain_part_matches = chain_part.matches(chain_input_string, + context, + with_raw_matches=True) + chain_found, chain_input_string, offset = \ + self._to_next_chain_part(chain_part, chain_part_matches, raw_chain_part_matches, chain_found, + input_string, chain_input_string, offset, current_chain_matches) except _InvalidChainException: valid_chain = False if current_chain_matches: offset = current_chain_matches[0].raw_end break - is_chain_start = False if not chain_found: break if current_chain_matches and valid_chain: @@ -217,38 +92,66 @@ class Chain(Pattern): return chain_matches - def _match_parent(self, match, yield_parent): + def _to_next_chain_part(self, chain_part, chain_part_matches, raw_chain_part_matches, chain_found, + input_string, chain_input_string, offset, current_chain_matches): + Chain._fix_matches_offset(chain_part_matches, input_string, offset) + Chain._fix_matches_offset(raw_chain_part_matches, input_string, offset) + + if raw_chain_part_matches: + grouped_matches_dict = self._group_by_match_index(chain_part_matches) + grouped_raw_matches_dict = self._group_by_match_index(raw_chain_part_matches) + + for match_index, grouped_raw_matches in grouped_raw_matches_dict.items(): + chain_found = True + offset = grouped_raw_matches[-1].raw_end + chain_input_string = input_string[offset:] + + if not chain_part.is_hidden: + grouped_matches = grouped_matches_dict.get(match_index, []) + if self._chain_breaker_eval(current_chain_matches + grouped_matches): + current_chain_matches.extend(grouped_matches) + return chain_found, chain_input_string, offset + + def _process_match(self, match, match_index, child=False): """ - Handle a parent match + Handle a match :param match: :type match: - :param yield_parent: - :type yield_parent: + :param match_index: + :type match_index: + :param child: + :type child: :return: :rtype: """ - ret = super(Chain, self)._match_parent(match, yield_parent) - original_children = Matches(match.children) - original_end = match.end - while not ret and match.children: - last_pattern = match.children[-1].pattern - last_pattern_children = [child for child in match.children if child.pattern == last_pattern] - last_pattern_groups_iter = itertools.groupby(last_pattern_children, lambda child: child.match_index) - last_pattern_groups = {} - for index, matches in last_pattern_groups_iter: - last_pattern_groups[index] = list(matches) + # pylint: disable=too-many-locals + ret = super(Chain, self)._process_match(match, match_index, child=child) + if ret: + return True - for index in reversed(list(last_pattern_groups)): - last_matches = list(last_pattern_groups[index]) - for last_match in last_matches: - match.children.remove(last_match) - match.end = match.children[-1].end if match.children else match.start - ret = super(Chain, self)._match_parent(match, yield_parent) - if ret: - return True - match.children = original_children - match.end = original_end - return ret + if match.children: + last_pattern = match.children[-1].pattern + last_pattern_groups = self._group_by_match_index( + [child_ for child_ in match.children if child_.pattern == last_pattern] + ) + + if last_pattern_groups: + original_children = Matches(match.children) + original_end = match.end + + for index in reversed(list(last_pattern_groups)): + last_matches = last_pattern_groups[index] + for last_match in last_matches: + match.children.remove(last_match) + match.end = match.children[-1].end if match.children else match.start + ret = super(Chain, self)._process_match(match, match_index, child=child) + if ret: + return True + + match.children = original_children + match.end = original_end + + return False def _build_chain_match(self, current_chain_matches, input_string): start = None @@ -282,46 +185,11 @@ class Chain(Pattern): Chain._fix_matches_offset(chain_part_match.children, input_string, offset) @staticmethod - def _match_chain_part(is_chain_start, chain_part, chain_input_string, context): - chain_part_matches, raw_chain_part_matches = chain_part.pattern.matches(chain_input_string, context, - with_raw_matches=True) - chain_part_matches = Chain._truncate_chain_part_matches(is_chain_start, chain_part_matches, chain_part, - chain_input_string) - raw_chain_part_matches = Chain._truncate_chain_part_matches(is_chain_start, raw_chain_part_matches, chain_part, - chain_input_string) - - Chain._validate_chain_part_matches(raw_chain_part_matches, chain_part) - return chain_part_matches, raw_chain_part_matches - - @staticmethod - def _truncate_chain_part_matches(is_chain_start, chain_part_matches, chain_part, chain_input_string): - if not chain_part_matches: - return chain_part_matches - - if not is_chain_start: - separator = chain_input_string[0:chain_part_matches[0].initiator.raw_start] - if separator: - return [] - - j = 1 - for i in range(0, len(chain_part_matches) - 1): - separator = chain_input_string[chain_part_matches[i].initiator.raw_end: - chain_part_matches[i + 1].initiator.raw_start] - if separator: - break - j += 1 - truncated = chain_part_matches[:j] - if chain_part.repeater_end is not None: - truncated = [m for m in truncated if m.match_index < chain_part.repeater_end] - return truncated - - @staticmethod - def _validate_chain_part_matches(chain_part_matches, chain_part): - max_match_index = -1 - if chain_part_matches: - max_match_index = max([m.match_index for m in chain_part_matches]) - if max_match_index + 1 < chain_part.repeater_start: - raise _InvalidChainException + def _group_by_match_index(matches): + grouped_matches_dict = dict() + for match_index, match in itertools.groupby(matches, lambda m: m.match_index): + grouped_matches_dict[match_index] = list(match) + return grouped_matches_dict @property def match_options(self): @@ -338,7 +206,7 @@ class Chain(Pattern): return "<%s%s:%s>" % (self.__class__.__name__, defined, self.parts) -class ChainPart(object): +class ChainPart(BasePattern): """ Part of a pattern chain. """ @@ -350,6 +218,51 @@ class ChainPart(object): self.repeater_end = 1 self._hidden = False + @property + def _is_chain_start(self): + return self._chain.parts[0] == self + + def matches(self, input_string, context=None, with_raw_matches=False): + matches, raw_matches = self.pattern.matches(input_string, context=context, with_raw_matches=True) + + matches = self._truncate_repeater(matches, input_string) + raw_matches = self._truncate_repeater(raw_matches, input_string) + + self._validate_repeater(raw_matches) + + if with_raw_matches: + return matches, raw_matches + + return matches + + def _truncate_repeater(self, matches, input_string): + if not matches: + return matches + + if not self._is_chain_start: + separator = input_string[0:matches[0].initiator.raw_start] + if separator: + return [] + + j = 1 + for i in range(0, len(matches) - 1): + separator = input_string[matches[i].initiator.raw_end: + matches[i + 1].initiator.raw_start] + if separator: + break + j += 1 + truncated = matches[:j] + if self.repeater_end is not None: + truncated = [m for m in truncated if m.match_index < self.repeater_end] + return truncated + + def _validate_repeater(self, matches): + max_match_index = -1 + if matches: + max_match_index = max([m.match_index for m in matches]) + if max_match_index + 1 < self.repeater_start: + raise _InvalidChainException + def chain(self): """ Add patterns chain, using configuration from this chain diff --git a/libs/common/rebulk/formatters.py b/libs/common/rebulk/formatters.py index 47046942..7175a54a 100644 --- a/libs/common/rebulk/formatters.py +++ b/libs/common/rebulk/formatters.py @@ -15,9 +15,19 @@ def formatters(*chained_formatters): :return: :rtype: """ + def formatters_chain(input_string): # pylint:disable=missing-docstring for chained_formatter in chained_formatters: input_string = chained_formatter(input_string) return input_string return formatters_chain + + +def default_formatter(input_string): + """ + Default formatter + :param input_string: + :return: + """ + return input_string diff --git a/libs/common/rebulk/introspector.py b/libs/common/rebulk/introspector.py index 64b9836f..bfefcb75 100644 --- a/libs/common/rebulk/introspector.py +++ b/libs/common/rebulk/introspector.py @@ -3,7 +3,7 @@ """ Introspect rebulk object to retrieve capabilities. """ -from abc import ABCMeta, abstractproperty +from abc import ABCMeta, abstractmethod from collections import defaultdict import six @@ -16,7 +16,8 @@ class Description(object): """ Abstract class for a description. """ - @abstractproperty + @property + @abstractmethod def properties(self): # pragma: no cover """ Properties of described object. diff --git a/libs/common/rebulk/loose.py b/libs/common/rebulk/loose.py index 427b69a0..423b4ea7 100644 --- a/libs/common/rebulk/loose.py +++ b/libs/common/rebulk/loose.py @@ -4,12 +4,12 @@ Various utilities functions """ - import sys -import inspect +from inspect import isclass try: from inspect import getfullargspec as getargspec + _fullargspec_supported = True except ImportError: _fullargspec_supported = False @@ -55,8 +55,8 @@ def call(function, *args, **kwargs): :return: sale vakye as default function call :rtype: object """ - func = constructor_args if inspect.isclass(function) else function_args - call_args, call_kwargs = func(function, *args, **kwargs) + func = constructor_args if isclass(function) else function_args + call_args, call_kwargs = func(function, *args, ignore_unused=True, **kwargs) # @see #20 return function(*call_args, **call_kwargs) @@ -145,6 +145,8 @@ if not _fullargspec_supported: else: call_args = args[:len(argspec.args) - (1 if constructor else 0)] return call_args, call_kwarg + + argspec_args = argspec_args_legacy @@ -215,9 +217,12 @@ def filter_index(collection, predicate=None, index=None): return collection -def set_defaults(defaults, kwargs): +def set_defaults(defaults, kwargs, override=False): """ Set defaults from defaults dict to kwargs dict + + :param override: + :type override: :param defaults: :type defaults: :param kwargs: @@ -225,12 +230,13 @@ def set_defaults(defaults, kwargs): :return: :rtype: """ + if 'clear' in defaults.keys() and defaults.pop('clear'): + kwargs.clear() for key, value in defaults.items(): - if key not in kwargs and value is not None: + if key in kwargs: + if isinstance(value, list) and isinstance(kwargs[key], list): + kwargs[key] = list(value) + kwargs[key] + elif isinstance(value, dict) and isinstance(kwargs[key], dict): + set_defaults(value, kwargs[key]) + if key not in kwargs or override: kwargs[key] = value - elif isinstance(value, list) and isinstance(kwargs[key], list): - kwargs[key] = list(value) + kwargs[key] - elif isinstance(value, dict) and isinstance(kwargs[key], dict): - set_defaults(value, kwargs[key]) - elif key in kwargs and value is None: - kwargs[key] = None diff --git a/libs/common/rebulk/match.py b/libs/common/rebulk/match.py index 8bf41245..d8e72df4 100644 --- a/libs/common/rebulk/match.py +++ b/libs/common/rebulk/match.py @@ -815,6 +815,24 @@ class Match(object): return filter_index(ret, predicate, index) + def tagged(self, *tags): + """ + Check if this match has at least one of the provided tags + + :param tags: + :return: True if at least one tag is defined, False otherwise. + """ + return any(tag in self.tags for tag in tags) + + def named(self, *names): + """ + Check if one of the children match has one of the provided name + + :param names: + :return: True if at least one child is named with a given name is defined, False otherwise. + """ + return any(name in self.names for name in names) + def __len__(self): return self.end - self.start diff --git a/libs/common/rebulk/pattern.py b/libs/common/rebulk/pattern.py index 57b274e8..beb8b273 100644 --- a/libs/common/rebulk/pattern.py +++ b/libs/common/rebulk/pattern.py @@ -10,14 +10,39 @@ from abc import ABCMeta, abstractmethod, abstractproperty import six from . import debug +from .formatters import default_formatter from .loose import call, ensure_list, ensure_dict from .match import Match from .remodule import re, REGEX_AVAILABLE from .utils import find_all, is_iterable, get_first_defined +from .validators import allways_true @six.add_metaclass(ABCMeta) -class Pattern(object): +class BasePattern(object): + """ + Base class for Pattern like objects + """ + + @abstractmethod + def matches(self, input_string, context=None, with_raw_matches=False): + """ + Computes all matches for a given input + + :param input_string: the string to parse + :type input_string: str + :param context: the context + :type context: dict + :param with_raw_matches: should return details + :type with_raw_matches: dict + :return: matches based on input_string for this pattern + :rtype: iterator[Match] + """ + pass + + +@six.add_metaclass(ABCMeta) +class Pattern(BasePattern): """ Definition of a particular pattern to search for. """ @@ -25,7 +50,7 @@ class Pattern(object): def __init__(self, name=None, tags=None, formatter=None, value=None, validator=None, children=False, every=False, private_parent=False, private_children=False, private=False, private_names=None, ignore_names=None, marker=False, format_all=False, validate_all=False, disabled=lambda context: False, log_level=None, - properties=None, post_processor=None, **kwargs): + properties=None, post_processor=None, pre_match_processor=None, post_match_processor=None, **kwargs): """ :param name: Name of this pattern :type name: str @@ -66,15 +91,19 @@ class Pattern(object): :type disabled: bool|function :param log_lvl: Log level associated to this pattern :type log_lvl: int - :param post_process: Post processing function + :param post_processor: Post processing function :type post_processor: func + :param pre_match_processor: Pre match processing function + :type pre_match_processor: func + :param post_match_processor: Post match processing function + :type post_match_processor: func """ # pylint:disable=too-many-locals,unused-argument self.name = name self.tags = ensure_list(tags) - self.formatters, self._default_formatter = ensure_dict(formatter, lambda x: x) + self.formatters, self._default_formatter = ensure_dict(formatter, default_formatter) self.values, self._default_value = ensure_dict(value, None) - self.validators, self._default_validator = ensure_dict(validator, lambda match: True) + self.validators, self._default_validator = ensure_dict(validator, allways_true) self.every = every self.children = children self.private = private @@ -96,6 +125,14 @@ class Pattern(object): self.post_processor = None else: self.post_processor = post_processor + if not callable(pre_match_processor): + self.pre_match_processor = None + else: + self.pre_match_processor = pre_match_processor + if not callable(post_match_processor): + self.post_match_processor = None + else: + self.post_match_processor = post_match_processor @property def log_level(self): @@ -106,83 +143,6 @@ class Pattern(object): """ return self._log_level if self._log_level is not None else debug.LOG_LEVEL - def _yield_children(self, match): - """ - Does this match has children - :param match: - :type match: - :return: - :rtype: - """ - return match.children and (self.children or self.every) - - def _yield_parent(self): - """ - Does this mat - :param match: - :type match: - :return: - :rtype: - """ - return not self.children or self.every - - def _match_parent(self, match, yield_parent): - """ - Handle a parent match - :param match: - :type match: - :param yield_parent: - :type yield_parent: - :return: - :rtype: - """ - if not match or match.value == "": - return False - - pattern_value = get_first_defined(self.values, [match.name, '__parent__', None], - self._default_value) - if pattern_value: - match.value = pattern_value - - if yield_parent or self.format_all: - match.formatter = get_first_defined(self.formatters, [match.name, '__parent__', None], - self._default_formatter) - if yield_parent or self.validate_all: - validator = get_first_defined(self.validators, [match.name, '__parent__', None], - self._default_validator) - if validator and not validator(match): - return False - return True - - def _match_child(self, child, yield_children): - """ - Handle a children match - :param child: - :type child: - :param yield_children: - :type yield_children: - :return: - :rtype: - """ - if not child or child.value == "": - return False - - pattern_value = get_first_defined(self.values, [child.name, '__children__', None], - self._default_value) - if pattern_value: - child.value = pattern_value - - if yield_children or self.format_all: - child.formatter = get_first_defined(self.formatters, [child.name, '__children__', None], - self._default_formatter) - - if yield_children or self.validate_all: - validator = get_first_defined(self.validators, [child.name, '__children__', None], - self._default_validator) - if validator and not validator(child): - return False - return True - def matches(self, input_string, context=None, with_raw_matches=False): """ Computes all matches for a given input @@ -200,41 +160,168 @@ class Pattern(object): matches = [] raw_matches = [] + for pattern in self.patterns: - yield_parent = self._yield_parent() - match_index = -1 + match_index = 0 for match in self._match(pattern, input_string, context): - match_index += 1 - match.match_index = match_index raw_matches.append(match) - yield_children = self._yield_children(match) - if not self._match_parent(match, yield_parent): - continue - validated = True - for child in match.children: - if not self._match_child(child, yield_children): - validated = False - break - if validated: - if self.private_parent: - match.private = True - if self.private_children: - for child in match.children: - child.private = True - if yield_parent or self.private_parent: - matches.append(match) - if yield_children or self.private_children: - for child in match.children: - child.match_index = match_index - matches.append(child) - matches = self._matches_post_process(matches) - self._matches_privatize(matches) - self._matches_ignore(matches) + matches.extend(self._process_matches(match, match_index)) + match_index += 1 + + matches = self._post_process_matches(matches) + if with_raw_matches: return matches, raw_matches return matches - def _matches_post_process(self, matches): + @property + def _should_include_children(self): + """ + Check if children matches from this pattern should be included in matches results. + :param match: + :type match: + :return: + :rtype: + """ + return self.children or self.every + + @property + def _should_include_parent(self): + """ + Check is a match from this pattern should be included in matches results. + :param match: + :type match: + :return: + :rtype: + """ + return not self.children or self.every + + @staticmethod + def _match_config_property_keys(match, child=False): + if match.name: + yield match.name + if child: + yield '__children__' + else: + yield '__parent__' + yield None + + @staticmethod + def _process_match_index(match, match_index): + """ + Process match index from this pattern process state. + + :param match: + :return: + """ + match.match_index = match_index + + def _process_match_private(self, match, child=False): + """ + Process match privacy from this pattern configuration. + + :param match: + :param child: + :return: + """ + + if match.name and match.name in self.private_names or \ + not child and self.private_parent or \ + child and self.private_children: + match.private = True + + def _process_match_value(self, match, child=False): + """ + Process match value from this pattern configuration. + :param match: + :return: + """ + keys = self._match_config_property_keys(match, child=child) + pattern_value = get_first_defined(self.values, keys, self._default_value) + if pattern_value: + match.value = pattern_value + + def _process_match_formatter(self, match, child=False): + """ + Process match formatter from this pattern configuration. + + :param match: + :return: + """ + included = self._should_include_children if child else self._should_include_parent + if included or self.format_all: + keys = self._match_config_property_keys(match, child=child) + match.formatter = get_first_defined(self.formatters, keys, self._default_formatter) + + def _process_match_validator(self, match, child=False): + """ + Process match validation from this pattern configuration. + + :param match: + :return: True if match is validated by the configured validator, False otherwise. + """ + included = self._should_include_children if child else self._should_include_parent + if included or self.validate_all: + keys = self._match_config_property_keys(match, child=child) + validator = get_first_defined(self.validators, keys, self._default_validator) + if validator and not validator(match): + return False + return True + + def _process_match(self, match, match_index, child=False): + """ + Process match from this pattern by setting all properties from defined configuration + (index, private, value, formatter, validator, ...). + + :param match: + :type match: + :return: True if match is validated by the configured validator, False otherwise. + :rtype: + """ + self._process_match_index(match, match_index) + self._process_match_private(match, child) + self._process_match_value(match, child) + self._process_match_formatter(match, child) + return self._process_match_validator(match, child) + + @staticmethod + def _process_match_processor(match, processor): + if processor: + ret = processor(match) + if ret is not None: + return ret + return match + + def _process_matches(self, match, match_index): + """ + Process and generate all matches for the given unprocessed match. + :param match: + :param match_index: + :return: Process and dispatched matches. + """ + match = self._process_match_processor(match, self.pre_match_processor) + if not match: + return + + if not self._process_match(match, match_index): + return + + for child in match.children: + if not self._process_match(child, match_index, child=True): + return + + match = self._process_match_processor(match, self.post_match_processor) + if not match: + return + + if (self._should_include_parent or self.private_parent) and match.name not in self.ignore_names: + yield match + if self._should_include_children or self.private_children: + children = [x for x in match.children if x.name not in self.ignore_names] + for child in children: + yield child + + def _post_process_matches(self, matches): """ Post process matches with user defined function :param matches: @@ -246,32 +333,6 @@ class Pattern(object): return self.post_processor(matches, self) return matches - def _matches_privatize(self, matches): - """ - Mark matches included in private_names with private flag. - :param matches: - :type matches: - :return: - :rtype: - """ - if self.private_names: - for match in matches: - if match.name in self.private_names: - match.private = True - - def _matches_ignore(self, matches): - """ - Ignore matches included in ignore_names. - :param matches: - :type matches: - :return: - :rtype: - """ - if self.ignore_names: - for match in list(matches): - if match.name in self.ignore_names: - matches.remove(match) - @abstractproperty def patterns(self): # pragma: no cover """ @@ -306,7 +367,7 @@ class Pattern(object): @abstractmethod def _match(self, pattern, input_string, context=None): # pragma: no cover """ - Computes all matches for a given pattern and input + Computes all unprocess matches for a given pattern and input. :param pattern: the pattern to use :param input_string: the string to parse @@ -350,7 +411,9 @@ class StringPattern(Pattern): def _match(self, pattern, input_string, context=None): for index in find_all(input_string, pattern, **self._kwargs): - yield Match(index, index + len(pattern), pattern=self, input_string=input_string, **self._match_kwargs) + match = Match(index, index + len(pattern), pattern=self, input_string=input_string, **self._match_kwargs) + if match: + yield match class RePattern(Pattern): @@ -411,15 +474,18 @@ class RePattern(Pattern): for start, end in match_object.spans(i): child_match = Match(start, end, name=name, parent=main_match, pattern=self, input_string=input_string, **self._children_match_kwargs) - main_match.children.append(child_match) + if child_match: + main_match.children.append(child_match) else: start, end = match_object.span(i) if start > -1 and end > -1: child_match = Match(start, end, name=name, parent=main_match, pattern=self, input_string=input_string, **self._children_match_kwargs) - main_match.children.append(child_match) + if child_match: + main_match.children.append(child_match) - yield main_match + if main_match: + yield main_match class FunctionalPattern(Pattern): @@ -457,14 +523,18 @@ class FunctionalPattern(Pattern): if self._match_kwargs: options = self._match_kwargs.copy() options.update(args) - yield Match(pattern=self, input_string=input_string, **options) + match = Match(pattern=self, input_string=input_string, **options) + if match: + yield match else: kwargs = self._match_kwargs if isinstance(args[-1], dict): kwargs = dict(kwargs) kwargs.update(args[-1]) args = args[:-1] - yield Match(*args, pattern=self, input_string=input_string, **kwargs) + match = Match(*args, pattern=self, input_string=input_string, **kwargs) + if match: + yield match def filter_match_kwargs(kwargs, children=False): diff --git a/libs/common/rebulk/rebulk.py b/libs/common/rebulk/rebulk.py index 42fb6440..a6a0fd2f 100644 --- a/libs/common/rebulk/rebulk.py +++ b/libs/common/rebulk/rebulk.py @@ -5,20 +5,16 @@ Entry point functions and classes for Rebulk """ from logging import getLogger +from .builder import Builder from .match import Matches - -from .pattern import RePattern, StringPattern, FunctionalPattern -from .chain import Chain - from .processors import ConflictSolver, PrivateRemover -from .loose import set_defaults -from .utils import extend_safe from .rules import Rules +from .utils import extend_safe log = getLogger(__name__).log -class Rebulk(object): +class Rebulk(Builder): r""" Regular expression, string and function based patterns are declared in a ``Rebulk`` object. It use a fluent API to chain ``string``, ``regex``, and ``functional`` methods to define various patterns types. @@ -44,6 +40,7 @@ class Rebulk(object): >>> bulk.matches("the lakers are from la") [, ] """ + # pylint:disable=protected-access def __init__(self, disabled=lambda context: False, default_rules=True): @@ -56,6 +53,7 @@ class Rebulk(object): :return: :rtype: """ + super(Rebulk, self).__init__() if not callable(disabled): self.disabled = lambda context: disabled else: @@ -64,11 +62,6 @@ class Rebulk(object): self._rules = Rules() if default_rules: self.rules(ConflictSolver, PrivateRemover) - self._defaults = {} - self._regex_defaults = {} - self._string_defaults = {} - self._functional_defaults = {} - self._chain_defaults = {} self._rebulks = [] def pattern(self, *pattern): @@ -83,172 +76,6 @@ class Rebulk(object): self._patterns.extend(pattern) return self - def defaults(self, **kwargs): - """ - Define default keyword arguments for all patterns - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._defaults = kwargs - return self - - def regex_defaults(self, **kwargs): - """ - Define default keyword arguments for functional patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._regex_defaults = kwargs - return self - - def regex(self, *pattern, **kwargs): - """ - Add re pattern - - :param pattern: - :type pattern: - :return: self - :rtype: Rebulk - """ - self.pattern(self.build_re(*pattern, **kwargs)) - return self - - def build_re(self, *pattern, **kwargs): - """ - Builds a new regular expression pattern - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._regex_defaults, kwargs) - set_defaults(self._defaults, kwargs) - return RePattern(*pattern, **kwargs) - - def string_defaults(self, **kwargs): - """ - Define default keyword arguments for string patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._string_defaults = kwargs - return self - - def string(self, *pattern, **kwargs): - """ - Add string pattern - - :param pattern: - :type pattern: - :return: self - :rtype: Rebulk - """ - self.pattern(self.build_string(*pattern, **kwargs)) - return self - - def build_string(self, *pattern, **kwargs): - """ - Builds a new string pattern - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._string_defaults, kwargs) - set_defaults(self._defaults, kwargs) - return StringPattern(*pattern, **kwargs) - - def functional_defaults(self, **kwargs): - """ - Define default keyword arguments for functional patterns. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._functional_defaults = kwargs - return self - - def functional(self, *pattern, **kwargs): - """ - Add functional pattern - - :param pattern: - :type pattern: - :return: self - :rtype: Rebulk - """ - self.pattern(self.build_functional(*pattern, **kwargs)) - return self - - def build_functional(self, *pattern, **kwargs): - """ - Builds a new functional pattern - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._functional_defaults, kwargs) - set_defaults(self._defaults, kwargs) - return FunctionalPattern(*pattern, **kwargs) - - def chain_defaults(self, **kwargs): - """ - Define default keyword arguments for patterns chain. - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - self._chain_defaults = kwargs - return self - - def chain(self, **kwargs): - """ - Add patterns chain, using configuration of this rebulk - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - chain = self.build_chain(**kwargs) - self._patterns.append(chain) - return chain - - def build_chain(self, **kwargs): - """ - Builds a new patterns chain - - :param pattern: - :type pattern: - :param kwargs: - :type kwargs: - :return: - :rtype: - """ - set_defaults(self._chain_defaults, kwargs) - set_defaults(self._defaults, kwargs) - return Chain(self, **kwargs) - def rules(self, *rules): """ Add rules as a module, class or instance. diff --git a/libs/common/rebulk/test/test_chain.py b/libs/common/rebulk/test/test_chain.py index 2715abc2..f3995546 100644 --- a/libs/common/rebulk/test/test_chain.py +++ b/libs/common/rebulk/test/test_chain.py @@ -2,11 +2,11 @@ # -*- coding: utf-8 -*- # pylint: disable=no-self-use, pointless-statement, missing-docstring, no-member, len-as-condition import re - from functools import partial +from rebulk.pattern import FunctionalPattern, StringPattern, RePattern +from ..rebulk import Rebulk from ..validators import chars_surround -from ..rebulk import Rebulk, FunctionalPattern, RePattern, StringPattern def test_chain_close(): @@ -63,18 +63,61 @@ def test_build_chain(): def test_chain_defaults(): rebulk = Rebulk() - rebulk.defaults(validator=lambda x: True, ignore_names=['testIgnore'], children=True) + rebulk.defaults(validator=lambda x: x.value.startswith('t'), ignore_names=['testIgnore'], children=True) - rebulk.chain()\ + rebulk.chain() \ .regex("(?Ptest)") \ .regex(" ").repeater("*") \ + .regex("(?Pbest)") \ + .regex(" ").repeater("*") \ .regex("(?PtestIgnore)") - matches = rebulk.matches("test testIgnore") + matches = rebulk.matches("test best testIgnore") assert len(matches) == 1 assert matches[0].name == "test" +def test_chain_with_validators(): + def chain_validator(match): + return match.value.startswith('t') and match.value.endswith('t') + + def default_validator(match): + return match.value.startswith('t') and match.value.endswith('g') + + def custom_validator(match): + return match.value.startswith('b') and match.value.endswith('t') + + rebulk = Rebulk() + rebulk.defaults(children=True, validator=default_validator) + + rebulk.chain(validate_all=True, validator={'__parent__': chain_validator}) \ + .regex("(?Ptesting)", validator=default_validator).repeater("+") \ + .regex(" ").repeater("+") \ + .regex("(?Pbest)", validator=custom_validator).repeater("+") + matches = rebulk.matches("some testing best end") + + assert len(matches) == 2 + assert matches[0].name == "test" + assert matches[1].name == "best" + + +def test_matches_docs(): + rebulk = Rebulk().regex_defaults(flags=re.IGNORECASE) \ + .defaults(children=True, formatter={'episode': int, 'version': int}) \ + .chain() \ + .regex(r'e(?P\d{1,4})').repeater(1) \ + .regex(r'v(?P\d+)').repeater('?') \ + .regex(r'[ex-](?P\d{1,4})').repeater('*') \ + .close() # .repeater(1) could be omitted as it's the default behavior + + result = rebulk.matches("This is E14v2-15-16-17").to_dict() # converts matches to dict + + assert 'episode' in result + assert result['episode'] == [14, 15, 16, 17] + assert 'version' in result + assert result['version'] == 2 + + def test_matches(): rebulk = Rebulk() @@ -144,8 +187,8 @@ def test_matches(): def test_matches_2(): rebulk = Rebulk() \ .regex_defaults(flags=re.IGNORECASE) \ - .chain(children=True, formatter={'episode': int}) \ - .defaults(formatter={'version': int}) \ + .defaults(children=True, formatter={'episode': int, 'version': int}) \ + .chain() \ .regex(r'e(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'[ex-](?P\d{1,4})').repeater('*') \ @@ -173,25 +216,32 @@ def test_matches_2(): def test_matches_3(): alt_dash = (r'@', r'[\W_]') # abbreviation - rebulk = Rebulk() + match_names = ['season', 'episode'] + other_names = ['screen_size', 'video_codec', 'audio_codec', 'audio_channels', 'container', 'date'] - rebulk.chain(formatter={'season': int, 'episode': int}, - tags=['SxxExx'], - abbreviations=[alt_dash], - private_names=['episodeSeparator', 'seasonSeparator'], - children=True, - private_parent=True, - conflict_solver=lambda match, other: match - if match.name in ['season', 'episode'] and other.name in - ['screen_size', 'video_codec', 'audio_codec', - 'audio_channels', 'container', 'date'] - else '__default__') \ + rebulk = Rebulk() + rebulk.defaults(formatter={'season': int, 'episode': int}, + tags=['SxxExx'], + abbreviations=[alt_dash], + private_names=['episodeSeparator', 'seasonSeparator'], + children=True, + private_parent=True, + conflict_solver=lambda match, other: match + if match.name in match_names and other.name in other_names + else '__default__') + + rebulk.chain() \ + .defaults(children=True, private_parent=True) \ .regex(r'(?P\d+)@?x@?(?P\d+)') \ .regex(r'(?Px|-|\+|&)(?P\d+)').repeater('*') \ + .close() \ .chain() \ + .defaults(children=True, private_parent=True) \ .regex(r'S(?P\d+)@?(?:xE|Ex|E|x)@?(?P\d+)') \ .regex(r'(?:(?PxE|Ex|E|x|-|\+|&)(?P\d+))').repeater('*') \ + .close() \ .chain() \ + .defaults(children=True, private_parent=True) \ .regex(r'S(?P\d+)') \ .regex(r'(?PS|-|\+|&)(?P\d+)').repeater('*') @@ -240,11 +290,11 @@ def test_matches_4(): rebulk = Rebulk() rebulk.regex_defaults(flags=re.IGNORECASE) - rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator'], validate_all=True, - validator={'__parent__': seps_surround}, children=True, private_parent=True) + rebulk.defaults(validate_all=True, children=True) + rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator'], private_parent=True) - rebulk.chain(formatter={'episode': int, 'version': int}) \ - .defaults(validator=None) \ + rebulk.chain(validator={'__parent__': seps_surround}, formatter={'episode': int, 'version': int}) \ + .defaults(formatter={'episode': int, 'version': int}) \ .regex(r'e(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Pe|x|-)(?P\d{1,4})').repeater('*') @@ -262,11 +312,11 @@ def test_matches_5(): rebulk = Rebulk() rebulk.regex_defaults(flags=re.IGNORECASE) - rebulk.defaults(private_names=['episodeSeparator', 'seasonSeparator'], validate_all=True, - validator={'__parent__': seps_surround}, children=True, private_parent=True) - rebulk.chain(formatter={'episode': int, 'version': int}) \ - .defaults(validator=None) \ + rebulk.chain(private_names=['episodeSeparator', 'seasonSeparator'], validate_all=True, + validator={'__parent__': seps_surround}, children=True, private_parent=True, + formatter={'episode': int, 'version': int}) \ + .defaults(children=True, private_parent=True) \ .regex(r'e(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Pe|x|-)(?P\d{1,4})').repeater('{2,3}') @@ -288,7 +338,7 @@ def test_matches_6(): validator=None, children=True, private_parent=True) rebulk.chain(formatter={'episode': int, 'version': int}) \ - .defaults(validator=None) \ + .defaults(children=True, private_parent=True) \ .regex(r'e(?P\d{1,4})') \ .regex(r'v(?P\d+)').repeater('?') \ .regex(r'(?Pe|x|-)(?P\d{1,4})').repeater('{2,3}') diff --git a/libs/common/rebulk/test/test_debug.py b/libs/common/rebulk/test/test_debug.py index cd9e556d..8abdac5f 100644 --- a/libs/common/rebulk/test/test_debug.py +++ b/libs/common/rebulk/test/test_debug.py @@ -2,19 +2,15 @@ # -*- coding: utf-8 -*- # pylint: disable=no-self-use, pointless-statement, missing-docstring, protected-access, invalid-name, len-as-condition +from .default_rules_module import RuleRemove0 +from .. import debug +from ..match import Match from ..pattern import StringPattern from ..rebulk import Rebulk -from ..match import Match -from .. import debug -from .default_rules_module import RuleRemove0 class TestDebug(object): - - - #request.addfinalizer(disable_debug) - - + # request.addfinalizer(disable_debug) debug.DEBUG = True pattern = StringPattern(1, 3, value="es") @@ -38,43 +34,43 @@ class TestDebug(object): debug.DEBUG = False def test_pattern(self): - assert self.pattern.defined_at.lineno == 20 + assert self.pattern.defined_at.lineno > 0 assert self.pattern.defined_at.name == 'rebulk.test.test_debug' assert self.pattern.defined_at.filename.endswith('test_debug.py') - assert str(self.pattern.defined_at) == 'test_debug.py#L20' - assert repr(self.pattern) == '' + assert str(self.pattern.defined_at).startswith('test_debug.py#L') + assert repr(self.pattern).startswith(' 0 assert self.match.defined_at.name == 'rebulk.test.test_debug' assert self.match.defined_at.filename.endswith('test_debug.py') - assert str(self.match.defined_at) == 'test_debug.py#L22' + assert str(self.match.defined_at).startswith('test_debug.py#L') def test_rule(self): - assert self.rule.defined_at.lineno == 23 + assert self.rule.defined_at.lineno > 0 assert self.rule.defined_at.name == 'rebulk.test.test_debug' assert self.rule.defined_at.filename.endswith('test_debug.py') - assert str(self.rule.defined_at) == 'test_debug.py#L23' - assert repr(self.rule) == '' + assert str(self.rule.defined_at).startswith('test_debug.py#L') + assert repr(self.rule).startswith(' 0 assert self.rebulk._patterns[0].defined_at.name == 'rebulk.test.test_debug' assert self.rebulk._patterns[0].defined_at.filename.endswith('test_debug.py') - assert str(self.rebulk._patterns[0].defined_at) in ['test_debug.py#L26', 'test_debug.py#L27'] + assert str(self.rebulk._patterns[0].defined_at).startswith('test_debug.py#L') - assert self.rebulk._patterns[1].defined_at.lineno in [27, 28] + assert self.rebulk._patterns[1].defined_at.lineno > 0 assert self.rebulk._patterns[1].defined_at.name == 'rebulk.test.test_debug' assert self.rebulk._patterns[1].defined_at.filename.endswith('test_debug.py') - assert str(self.rebulk._patterns[1].defined_at) in ['test_debug.py#L27', 'test_debug.py#L28'] + assert str(self.rebulk._patterns[1].defined_at).startswith('test_debug.py#L') assert self.matches[0].defined_at == self.rebulk._patterns[0].defined_at assert self.matches[1].defined_at == self.rebulk._patterns[1].defined_at diff --git a/libs/common/rebulk/test/test_match.py b/libs/common/rebulk/test/test_match.py index 87273d54..8750733a 100644 --- a/libs/common/rebulk/test/test_match.py +++ b/libs/common/rebulk/test/test_match.py @@ -116,6 +116,9 @@ class TestMatchesClass(object): assert "tag1" in matches.tags assert "tag2" in matches.tags + assert self.match3.tagged("tag1") + assert not self.match3.tagged("start") + tag1 = matches.tagged("tag1") assert len(tag1) == 2 assert tag1[0] == self.match2 diff --git a/libs/common/rebulk/validators.py b/libs/common/rebulk/validators.py index 5fd3dcb6..b8959c54 100644 --- a/libs/common/rebulk/validators.py +++ b/libs/common/rebulk/validators.py @@ -62,9 +62,20 @@ def validators(*chained_validators): :return: :rtype: """ + def validator_chain(match): # pylint:disable=missing-docstring for chained_validator in chained_validators: if not chained_validator(match): return False return True + return validator_chain + + +def allways_true(match): # pylint:disable=unused-argument + """ + A validator which is allways true + :param match: + :return: + """ + return True From 968ec8a1d86c24c3e373ca3c7d51a86d30b015fc Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 21:11:01 -0500 Subject: [PATCH 34/47] Update vendored beautifulsoup4 to 4.11.1 Adds soupsieve 2.3.2.post1 --- libs/common/bs4/__init__.py | 588 ++++-- libs/common/bs4/builder/__init__.py | 354 +++- libs/common/bs4/builder/_html5lib.py | 103 +- libs/common/bs4/builder/_htmlparser.py | 220 +- libs/common/bs4/builder/_lxml.py | 180 +- libs/common/bs4/dammit.py | 395 +++- libs/common/bs4/diagnose.py | 79 +- libs/common/bs4/element.py | 1864 ++++++++++------- libs/common/bs4/formatter.py | 185 ++ libs/common/bs4/testing.py | 810 ------- libs/common/bs4/tests/__init__.py | 1192 ++++++++++- libs/common/bs4/tests/test_builder.py | 29 + .../common/bs4/tests/test_builder_registry.py | 73 +- libs/common/bs4/tests/test_dammit.py | 371 ++++ libs/common/bs4/tests/test_docs.py | 2 + libs/common/bs4/tests/test_element.py | 74 + libs/common/bs4/tests/test_formatter.py | 113 + libs/common/bs4/tests/test_html5lib.py | 133 +- libs/common/bs4/tests/test_htmlparser.py | 109 +- libs/common/bs4/tests/test_lxml.py | 145 +- libs/common/bs4/tests/test_navigablestring.py | 144 ++ libs/common/bs4/tests/test_pageelement.py | 751 +++++++ libs/common/bs4/tests/test_soup.py | 687 +++--- libs/common/bs4/tests/test_tag.py | 221 ++ libs/common/bs4/tests/test_tree.py | 1808 +++++----------- libs/common/soupsieve/__init__.py | 166 ++ libs/common/soupsieve/__meta__.py | 196 ++ libs/common/soupsieve/css_match.py | 1584 ++++++++++++++ libs/common/soupsieve/css_parser.py | 1310 ++++++++++++ libs/common/soupsieve/css_types.py | 407 ++++ libs/common/soupsieve/pretty.py | 137 ++ libs/common/soupsieve/py.typed | 0 libs/common/soupsieve/util.py | 116 + 33 files changed, 10852 insertions(+), 3694 deletions(-) create mode 100644 libs/common/bs4/formatter.py delete mode 100644 libs/common/bs4/testing.py create mode 100644 libs/common/bs4/tests/test_builder.py create mode 100644 libs/common/bs4/tests/test_dammit.py create mode 100644 libs/common/bs4/tests/test_element.py create mode 100644 libs/common/bs4/tests/test_formatter.py create mode 100644 libs/common/bs4/tests/test_navigablestring.py create mode 100644 libs/common/bs4/tests/test_pageelement.py create mode 100644 libs/common/bs4/tests/test_tag.py create mode 100644 libs/common/soupsieve/__init__.py create mode 100644 libs/common/soupsieve/__meta__.py create mode 100644 libs/common/soupsieve/css_match.py create mode 100644 libs/common/soupsieve/css_parser.py create mode 100644 libs/common/soupsieve/css_types.py create mode 100644 libs/common/soupsieve/pretty.py create mode 100644 libs/common/soupsieve/py.typed create mode 100644 libs/common/soupsieve/util.py diff --git a/libs/common/bs4/__init__.py b/libs/common/bs4/__init__.py index 797a6826..b3c9feb8 100644 --- a/libs/common/bs4/__init__.py +++ b/libs/common/bs4/__init__.py @@ -1,6 +1,5 @@ -"""Beautiful Soup -Elixir and Tonic -"The Screen-Scraper's Friend" +"""Beautiful Soup Elixir and Tonic - "The Screen-Scraper's Friend". + http://www.crummy.com/software/BeautifulSoup/ Beautiful Soup uses a pluggable XML or HTML parser to parse a @@ -8,32 +7,38 @@ Beautiful Soup uses a pluggable XML or HTML parser to parse a provides methods and Pythonic idioms that make it easy to navigate, search, and modify the parse tree. -Beautiful Soup works with Python 2.7 and up. It works better if lxml +Beautiful Soup works with Python 3.5 and up. It works better if lxml and/or html5lib is installed. For more than you ever wanted to know about Beautiful Soup, see the -documentation: -http://www.crummy.com/software/BeautifulSoup/bs4/doc/ - +documentation: http://www.crummy.com/software/BeautifulSoup/bs4/doc/ """ -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - __author__ = "Leonard Richardson (leonardr@segfault.org)" -__version__ = "4.6.3" -__copyright__ = "Copyright (c) 2004-2018 Leonard Richardson" +__version__ = "4.11.1" +__copyright__ = "Copyright (c) 2004-2022 Leonard Richardson" +# Use of this source code is governed by the MIT license. __license__ = "MIT" __all__ = ['BeautifulSoup'] +from collections import Counter import os import re import sys import traceback import warnings -from .builder import builder_registry, ParserRejectedMarkup +# The very first thing we do is give a useful error if someone is +# running this code under Python 2. +if sys.version_info.major < 3: + raise ImportError('You are trying to use a Python 3-specific version of Beautiful Soup under Python 2. This will not work. The final version of Beautiful Soup to support Python 2 was 4.9.3.') + +from .builder import ( + builder_registry, + ParserRejectedMarkup, + XMLParsedAsHTMLWarning, +) from .dammit import UnicodeDammit from .element import ( CData, @@ -44,28 +49,49 @@ from .element import ( NavigableString, PageElement, ProcessingInstruction, + PYTHON_SPECIFIC_ENCODINGS, ResultSet, + Script, + Stylesheet, SoupStrainer, Tag, + TemplateString, ) -# The very first thing we do is give a useful error if someone is -# running this code under Python 3 without converting it. -'You are trying to run the Python 2 version of Beautiful Soup under Python 3. This will not work.'!='You need to convert the code, either by installing it (`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).' - -class BeautifulSoup(Tag): +# Define some custom warnings. +class GuessedAtParserWarning(UserWarning): + """The warning issued when BeautifulSoup has to guess what parser to + use -- probably because no parser was specified in the constructor. """ - This class defines the basic interface called by the tree builders. - These methods will be called by the parser: - reset() - feed(markup) +class MarkupResemblesLocatorWarning(UserWarning): + """The warning issued when BeautifulSoup is given 'markup' that + actually looks like a resource locator -- a URL or a path to a file + on disk. + """ + + +class BeautifulSoup(Tag): + """A data structure representing a parsed HTML or XML document. + + Most of the methods you'll call on a BeautifulSoup object are inherited from + PageElement or Tag. + + Internally, this class defines the basic interface called by the + tree builders when converting an HTML/XML document into a data + structure. The interface abstracts away the differences between + parsers. To write a new tree builder, you'll need to understand + these methods as a whole. + + These methods will be called by the BeautifulSoup constructor: + * reset() + * feed(markup) The tree builder may call these methods from its feed() implementation: - handle_starttag(name, attrs) # See note about return value - handle_endtag(name) - handle_data(data) # Appends to the current data node - endData(containerClass=NavigableString) # Ends the current data node + * handle_starttag(name, attrs) # See note about return value + * handle_endtag(name) + * handle_data(data) # Appends to the current data node + * endData(containerClass) # Ends the current data node No matter how complicated the underlying parser is, you should be able to build a tree using 'start tag' events, 'end tag' events, @@ -75,56 +101,77 @@ class BeautifulSoup(Tag): like HTML's
    tag), call handle_starttag and then handle_endtag. """ + + # Since BeautifulSoup subclasses Tag, it's possible to treat it as + # a Tag with a .name. This name makes it clear the BeautifulSoup + # object isn't a real markup tag. ROOT_TAG_NAME = '[document]' # If the end-user gives no indication which tree builder they # want, look for one with these features. DEFAULT_BUILDER_FEATURES = ['html', 'fast'] + # A string containing all ASCII whitespace characters, used in + # endData() to detect data chunks that seem 'empty'. ASCII_SPACES = '\x20\x0a\x09\x0c\x0d' NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nThe code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, pass the additional argument 'features=\"%(parser)s\"' to the BeautifulSoup constructor.\n" - + def __init__(self, markup="", features=None, builder=None, parse_only=None, from_encoding=None, exclude_encodings=None, - **kwargs): + element_classes=None, **kwargs): """Constructor. :param markup: A string or a file-like object representing - markup to be parsed. + markup to be parsed. - :param features: Desirable features of the parser to be used. This - may be the name of a specific parser ("lxml", "lxml-xml", - "html.parser", or "html5lib") or it may be the type of markup - to be used ("html", "html5", "xml"). It's recommended that you - name a specific parser, so that Beautiful Soup gives you the - same results across platforms and virtual environments. + :param features: Desirable features of the parser to be + used. This may be the name of a specific parser ("lxml", + "lxml-xml", "html.parser", or "html5lib") or it may be the + type of markup to be used ("html", "html5", "xml"). It's + recommended that you name a specific parser, so that + Beautiful Soup gives you the same results across platforms + and virtual environments. - :param builder: A specific TreeBuilder to use instead of looking one - up based on `features`. You shouldn't need to use this. + :param builder: A TreeBuilder subclass to instantiate (or + instance to use) instead of looking one up based on + `features`. You only need to use this if you've implemented a + custom TreeBuilder. :param parse_only: A SoupStrainer. Only parts of the document - matching the SoupStrainer will be considered. This is useful - when parsing part of a document that would otherwise be too - large to fit into memory. + matching the SoupStrainer will be considered. This is useful + when parsing part of a document that would otherwise be too + large to fit into memory. :param from_encoding: A string indicating the encoding of the - document to be parsed. Pass this in if Beautiful Soup is - guessing wrongly about the document's encoding. + document to be parsed. Pass this in if Beautiful Soup is + guessing wrongly about the document's encoding. :param exclude_encodings: A list of strings indicating - encodings known to be wrong. Pass this in if you don't know - the document's encoding but you know Beautiful Soup's guess is - wrong. + encodings known to be wrong. Pass this in if you don't know + the document's encoding but you know Beautiful Soup's guess is + wrong. + + :param element_classes: A dictionary mapping BeautifulSoup + classes like Tag and NavigableString, to other classes you'd + like to be instantiated instead as the parse tree is + built. This is useful for subclassing Tag or NavigableString + to modify default behavior. :param kwargs: For backwards compatibility purposes, the - constructor accepts certain keyword arguments used in - Beautiful Soup 3. None of these arguments do anything in - Beautiful Soup 4 and there's no need to actually pass keyword - arguments into the constructor. + constructor accepts certain keyword arguments used in + Beautiful Soup 3. None of these arguments do anything in + Beautiful Soup 4; they will result in a warning and then be + ignored. + + Apart from this, any keyword arguments passed into the + BeautifulSoup constructor are propagated to the TreeBuilder + constructor. This makes it possible to configure a + TreeBuilder by passing in arguments, not just by saying which + one to use. """ - if 'convertEntities' in kwargs: + del kwargs['convertEntities'] warnings.warn( "BS4 does not respect the convertEntities argument to the " "BeautifulSoup constructor. Entities are always converted " @@ -163,10 +210,10 @@ class BeautifulSoup(Tag): if old_name in kwargs: warnings.warn( 'The "%s" argument to the BeautifulSoup constructor ' - 'has been renamed to "%s."' % (old_name, new_name)) - value = kwargs[old_name] - del kwargs[old_name] - return value + 'has been renamed to "%s."' % (old_name, new_name), + DeprecationWarning + ) + return kwargs.pop(old_name) return None parse_only = parse_only or deprecated_argument( @@ -179,13 +226,19 @@ class BeautifulSoup(Tag): warnings.warn("You provided Unicode markup but also provided a value for from_encoding. Your from_encoding will be ignored.") from_encoding = None - if len(kwargs) > 0: - arg = list(kwargs.keys()).pop() - raise TypeError( - "__init__() got an unexpected keyword argument '%s'" % arg) + self.element_classes = element_classes or dict() - if builder is None: - original_features = features + # We need this information to track whether or not the builder + # was specified well enough that we can omit the 'you need to + # specify a parser' warning. + original_builder = builder + original_features = features + + if isinstance(builder, type): + # A builder class was passed in; it needs to be instantiated. + builder_class = builder + builder = None + elif builder is None: if isinstance(features, str): features = [features] if features is None or len(features) == 0: @@ -196,9 +249,18 @@ class BeautifulSoup(Tag): "Couldn't find a tree builder with the features you " "requested: %s. Do you need to install a parser library?" % ",".join(features)) - builder = builder_class() - if not (original_features == builder.NAME or - original_features in builder.ALTERNATE_NAMES): + + # At this point either we have a TreeBuilder instance in + # builder, or we have a builder_class that we can instantiate + # with the remaining **kwargs. + if builder is None: + builder = builder_class(**kwargs) + if not original_builder and not ( + original_features == builder.NAME or + original_features in builder.ALTERNATE_NAMES + ) and markup: + # The user did not tell us which TreeBuilder to use, + # and we had to guess. Issue a warning. if builder.is_xml: markup_type = "XML" else: @@ -232,13 +294,18 @@ class BeautifulSoup(Tag): parser=builder.NAME, markup_type=markup_type ) - warnings.warn(self.NO_PARSER_SPECIFIED_WARNING % values, stacklevel=2) - + warnings.warn( + self.NO_PARSER_SPECIFIED_WARNING % values, + GuessedAtParserWarning, stacklevel=2 + ) + else: + if kwargs: + warnings.warn("Keyword arguments to the BeautifulSoup constructor will be ignored. These would normally be passed into the TreeBuilder constructor, but a TreeBuilder instance was passed in as `builder`.") + self.builder = builder self.is_xml = builder.is_xml self.known_xml = self.is_xml - self.builder.soup = self - + self._namespaces = dict() self.parse_only = parse_only if hasattr(markup, 'read'): # It's a file-type object. @@ -247,49 +314,42 @@ class BeautifulSoup(Tag): (isinstance(markup, bytes) and not b'<' in markup) or (isinstance(markup, str) and not '<' in markup) ): - # Print out warnings for a couple beginner problems + # Issue warnings for a couple beginner problems # involving passing non-markup to Beautiful Soup. # Beautiful Soup will still parse the input as markup, - # just in case that's what the user really wants. - if (isinstance(markup, str) - and not os.path.supports_unicode_filenames): - possible_filename = markup.encode("utf8") - else: - possible_filename = markup - is_file = False - try: - is_file = os.path.exists(possible_filename) - except Exception as e: - # This is almost certainly a problem involving - # characters not valid in filenames on this - # system. Just let it go. - pass - if is_file: - if isinstance(markup, str): - markup = markup.encode("utf8") - warnings.warn( - '"%s" looks like a filename, not markup. You should' - ' probably open this file and pass the filehandle into' - ' Beautiful Soup.' % markup) - self._check_markup_is_url(markup) + # since that is sometimes the intended behavior. + if not self._markup_is_url(markup): + self._markup_resembles_filename(markup) + rejections = [] + success = False for (self.markup, self.original_encoding, self.declared_html_encoding, self.contains_replacement_characters) in ( self.builder.prepare_markup( markup, from_encoding, exclude_encodings=exclude_encodings)): self.reset() + self.builder.initialize_soup(self) try: self._feed() + success = True break - except ParserRejectedMarkup: + except ParserRejectedMarkup as e: + rejections.append(e) pass + if not success: + other_exceptions = [str(e) for e in rejections] + raise ParserRejectedMarkup( + "The markup you provided was rejected by the parser. Trying a different parser or a different encoding may help.\n\nOriginal exception(s) from parser:\n " + "\n ".join(other_exceptions) + ) + # Clear out the markup and remove the builder's circular # reference to this object. self.markup = None self.builder.soup = None def __copy__(self): + """Copy a BeautifulSoup object by converting the document to a string and parsing it again.""" copy = type(self)( self.encode('utf-8'), builder=self.builder, from_encoding='utf-8' ) @@ -304,15 +364,31 @@ class BeautifulSoup(Tag): def __getstate__(self): # Frequently a tree builder can't be pickled. d = dict(self.__dict__) - if 'builder' in d and not self.builder.picklable: + if 'builder' in d and d['builder'] is not None and not self.builder.picklable: d['builder'] = None return d + + @classmethod + def _decode_markup(cls, markup): + """Ensure `markup` is bytes so it's safe to send into warnings.warn. - @staticmethod - def _check_markup_is_url(markup): - """ - Check if markup looks like it's actually a url and raise a warning - if so. Markup can be unicode or str (py2) / bytes (py3). + TODO: warnings.warn had this problem back in 2010 but it might not + anymore. + """ + if isinstance(markup, bytes): + decoded = markup.decode('utf-8', 'replace') + else: + decoded = markup + return decoded + + @classmethod + def _markup_is_url(cls, markup): + """Error-handling method to raise a warning if incoming markup looks + like a URL. + + :param markup: A string. + :return: Whether or not the markup resembles a URL + closely enough to justify a warning. """ if isinstance(markup, bytes): space = b' ' @@ -321,22 +397,54 @@ class BeautifulSoup(Tag): space = ' ' cant_start_with = ("http:", "https:") else: - return + return False if any(markup.startswith(prefix) for prefix in cant_start_with): if not space in markup: - if isinstance(markup, bytes): - decoded_markup = markup.decode('utf-8', 'replace') - else: - decoded_markup = markup warnings.warn( - '"%s" looks like a URL. Beautiful Soup is not an' - ' HTTP client. You should probably use an HTTP client like' - ' requests to get the document behind the URL, and feed' - ' that document to Beautiful Soup.' % decoded_markup + 'The input looks more like a URL than markup. You may want to use' + ' an HTTP client like requests to get the document behind' + ' the URL, and feed that document to Beautiful Soup.', + MarkupResemblesLocatorWarning ) + return True + return False + @classmethod + def _markup_resembles_filename(cls, markup): + """Error-handling method to raise a warning if incoming markup + resembles a filename. + + :param markup: A bytestring or string. + :return: Whether or not the markup resembles a filename + closely enough to justify a warning. + """ + path_characters = '/\\' + extensions = ['.html', '.htm', '.xml', '.xhtml', '.txt'] + if isinstance(markup, bytes): + path_characters = path_characters.encode("utf8") + extensions = [x.encode('utf8') for x in extensions] + filelike = False + if any(x in markup for x in path_characters): + filelike = True + else: + lower = markup.lower() + if any(lower.endswith(ext) for ext in extensions): + filelike = True + if filelike: + warnings.warn( + 'The input looks more like a filename than markup. You may' + ' want to open this file and pass the filehandle into' + ' Beautiful Soup.', + MarkupResemblesLocatorWarning + ) + return True + return False + def _feed(self): + """Internal method that parses previously set markup, creating a large + number of Tag and NavigableString objects. + """ # Convert the document to Unicode. self.builder.reset() @@ -347,49 +455,110 @@ class BeautifulSoup(Tag): self.popTag() def reset(self): + """Reset this object to a state as though it had never parsed any + markup. + """ Tag.__init__(self, self, self.builder, self.ROOT_TAG_NAME) self.hidden = 1 self.builder.reset() self.current_data = [] self.currentTag = None self.tagStack = [] + self.open_tag_counter = Counter() self.preserve_whitespace_tag_stack = [] + self.string_container_stack = [] self.pushTag(self) - def new_tag(self, name, namespace=None, nsprefix=None, attrs={}, **kwattrs): - """Create a new tag associated with this soup.""" + def new_tag(self, name, namespace=None, nsprefix=None, attrs={}, + sourceline=None, sourcepos=None, **kwattrs): + """Create a new Tag associated with this BeautifulSoup object. + + :param name: The name of the new Tag. + :param namespace: The URI of the new Tag's XML namespace, if any. + :param prefix: The prefix for the new Tag's XML namespace, if any. + :param attrs: A dictionary of this Tag's attribute values; can + be used instead of `kwattrs` for attributes like 'class' + that are reserved words in Python. + :param sourceline: The line number where this tag was + (purportedly) found in its source document. + :param sourcepos: The character position within `sourceline` where this + tag was (purportedly) found. + :param kwattrs: Keyword arguments for the new Tag's attribute values. + + """ kwattrs.update(attrs) - return Tag(None, self.builder, name, namespace, nsprefix, kwattrs) + return self.element_classes.get(Tag, Tag)( + None, self.builder, name, namespace, nsprefix, kwattrs, + sourceline=sourceline, sourcepos=sourcepos + ) - def new_string(self, s, subclass=NavigableString): - """Create a new NavigableString associated with this soup.""" - return subclass(s) + def string_container(self, base_class=None): + container = base_class or NavigableString + + # There may be a general override of NavigableString. + container = self.element_classes.get( + container, container + ) - def insert_before(self, successor): + # On top of that, we may be inside a tag that needs a special + # container class. + if self.string_container_stack and container is NavigableString: + container = self.builder.string_containers.get( + self.string_container_stack[-1].name, container + ) + return container + + def new_string(self, s, subclass=None): + """Create a new NavigableString associated with this BeautifulSoup + object. + """ + container = self.string_container(subclass) + return container(s) + + def insert_before(self, *args): + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ raise NotImplementedError("BeautifulSoup objects don't support insert_before().") - def insert_after(self, successor): + def insert_after(self, *args): + """This method is part of the PageElement API, but `BeautifulSoup` doesn't implement + it because there is nothing before or after it in the parse tree. + """ raise NotImplementedError("BeautifulSoup objects don't support insert_after().") def popTag(self): + """Internal method called by _popToTag when a tag is closed.""" tag = self.tagStack.pop() + if tag.name in self.open_tag_counter: + self.open_tag_counter[tag.name] -= 1 if self.preserve_whitespace_tag_stack and tag == self.preserve_whitespace_tag_stack[-1]: self.preserve_whitespace_tag_stack.pop() - #print "Pop", tag.name + if self.string_container_stack and tag == self.string_container_stack[-1]: + self.string_container_stack.pop() + #print("Pop", tag.name) if self.tagStack: self.currentTag = self.tagStack[-1] return self.currentTag def pushTag(self, tag): - #print "Push", tag.name - if self.currentTag: + """Internal method called by handle_starttag when a tag is opened.""" + #print("Push", tag.name) + if self.currentTag is not None: self.currentTag.contents.append(tag) self.tagStack.append(tag) self.currentTag = self.tagStack[-1] + if tag.name != self.ROOT_TAG_NAME: + self.open_tag_counter[tag.name] += 1 if tag.name in self.builder.preserve_whitespace_tags: self.preserve_whitespace_tag_stack.append(tag) + if tag.name in self.builder.string_containers: + self.string_container_stack.append(tag) - def endData(self, containerClass=NavigableString): + def endData(self, containerClass=None): + """Method called by the TreeBuilder when the end of a data segment + occurs. + """ if self.current_data: current_data = ''.join(self.current_data) # If whitespace is not preserved, and this string contains @@ -416,72 +585,93 @@ class BeautifulSoup(Tag): not self.parse_only.search(current_data)): return + containerClass = self.string_container(containerClass) o = containerClass(current_data) self.object_was_parsed(o) def object_was_parsed(self, o, parent=None, most_recent_element=None): - """Add an object to the parse tree.""" - parent = parent or self.currentTag - previous_element = most_recent_element or self._most_recent_element + """Method called by the TreeBuilder to integrate an object into the parse tree.""" + if parent is None: + parent = self.currentTag + if most_recent_element is not None: + previous_element = most_recent_element + else: + previous_element = self._most_recent_element next_element = previous_sibling = next_sibling = None if isinstance(o, Tag): next_element = o.next_element next_sibling = o.next_sibling previous_sibling = o.previous_sibling - if not previous_element: + if previous_element is None: previous_element = o.previous_element + fix = parent.next_element is not None + o.setup(parent, previous_element, next_element, previous_sibling, next_sibling) self._most_recent_element = o parent.contents.append(o) - if parent.next_sibling: - # This node is being inserted into an element that has - # already been parsed. Deal with any dangling references. - index = len(parent.contents)-1 - while index >= 0: - if parent.contents[index] is o: - break - index -= 1 - else: - raise ValueError( - "Error building tree: supposedly %r was inserted " - "into %r after the fact, but I don't see it!" % ( - o, parent - ) - ) - if index == 0: - previous_element = parent - previous_sibling = None - else: - previous_element = previous_sibling = parent.contents[index-1] - if index == len(parent.contents)-1: - next_element = parent.next_sibling - next_sibling = None - else: - next_element = next_sibling = parent.contents[index+1] + # Check if we are inserting into an already parsed node. + if fix: + self._linkage_fixer(parent) - o.previous_element = previous_element - if previous_element: - previous_element.next_element = o - o.next_element = next_element - if next_element: - next_element.previous_element = o - o.next_sibling = next_sibling - if next_sibling: - next_sibling.previous_sibling = o - o.previous_sibling = previous_sibling - if previous_sibling: - previous_sibling.next_sibling = o + def _linkage_fixer(self, el): + """Make sure linkage of this fragment is sound.""" + + first = el.contents[0] + child = el.contents[-1] + descendant = child + + if child is first and el.parent is not None: + # Parent should be linked to first child + el.next_element = child + # We are no longer linked to whatever this element is + prev_el = child.previous_element + if prev_el is not None and prev_el is not el: + prev_el.next_element = None + # First child should be linked to the parent, and no previous siblings. + child.previous_element = el + child.previous_sibling = None + + # We have no sibling as we've been appended as the last. + child.next_sibling = None + + # This index is a tag, dig deeper for a "last descendant" + if isinstance(child, Tag) and child.contents: + descendant = child._last_descendant(False) + + # As the final step, link last descendant. It should be linked + # to the parent's next sibling (if found), else walk up the chain + # and find a parent with a sibling. It should have no next sibling. + descendant.next_element = None + descendant.next_sibling = None + target = el + while True: + if target is None: + break + elif target.next_sibling is not None: + descendant.next_element = target.next_sibling + target.next_sibling.previous_element = child + break + target = target.parent def _popToTag(self, name, nsprefix=None, inclusivePop=True): """Pops the tag stack up to and including the most recent - instance of the given tag. If inclusivePop is false, pops the tag - stack up to but *not* including the most recent instqance of - the given tag.""" - #print "Popping to %s" % name + instance of the given tag. + + If there are no open tags with the given name, nothing will be + popped. + + :param name: Pop up to the most recent tag with this name. + :param nsprefix: The namespace prefix that goes with `name`. + :param inclusivePop: It this is false, pops the tag stack up + to but *not* including the most recent instqance of the + given tag. + + """ + #print("Popping to %s" % name) if name == self.ROOT_TAG_NAME: # The BeautifulSoup object itself can never be popped. return @@ -490,6 +680,8 @@ class BeautifulSoup(Tag): stack_size = len(self.tagStack) for i in range(stack_size - 1, 0, -1): + if not self.open_tag_counter.get(name): + break t = self.tagStack[i] if (name == t.name and nsprefix == t.prefix): if inclusivePop: @@ -499,16 +691,26 @@ class BeautifulSoup(Tag): return most_recently_popped - def handle_starttag(self, name, namespace, nsprefix, attrs): - """Push a start tag on to the stack. + def handle_starttag(self, name, namespace, nsprefix, attrs, sourceline=None, + sourcepos=None, namespaces=None): + """Called by the tree builder when a new tag is encountered. - If this method returns None, the tag was rejected by the + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + :param attrs: A dictionary of attribute values. + :param sourceline: The line number where this tag was found in its + source document. + :param sourcepos: The character position within `sourceline` where this + tag was found. + :param namespaces: A dictionary of all namespace prefix mappings + currently in scope in the document. + + If this method returns None, the tag was rejected by an active SoupStrainer. You should proceed as if the tag had not occurred in the document. For instance, if this was a self-closing tag, don't call handle_endtag. """ - - # print "Start tag %s: %s" % (name, attrs) + # print("Start tag %s: %s" % (name, attrs)) self.endData() if (self.parse_only and len(self.tagStack) <= 1 @@ -516,33 +718,53 @@ class BeautifulSoup(Tag): or not self.parse_only.search_tag(name, attrs))): return None - tag = Tag(self, self.builder, name, namespace, nsprefix, attrs, - self.currentTag, self._most_recent_element) + tag = self.element_classes.get(Tag, Tag)( + self, self.builder, name, namespace, nsprefix, attrs, + self.currentTag, self._most_recent_element, + sourceline=sourceline, sourcepos=sourcepos, + namespaces=namespaces + ) if tag is None: return tag - if self._most_recent_element: + if self._most_recent_element is not None: self._most_recent_element.next_element = tag self._most_recent_element = tag self.pushTag(tag) return tag def handle_endtag(self, name, nsprefix=None): - #print "End tag: " + name + """Called by the tree builder when an ending tag is encountered. + + :param name: Name of the tag. + :param nsprefix: Namespace prefix for the tag. + """ + #print("End tag: " + name) self.endData() self._popToTag(name, nsprefix) - + def handle_data(self, data): + """Called by the tree builder when a chunk of textual data is encountered.""" self.current_data.append(data) - + def decode(self, pretty_print=False, eventual_encoding=DEFAULT_OUTPUT_ENCODING, formatter="minimal"): - """Returns a string or Unicode representation of this document. - To get Unicode, pass None for encoding.""" + """Returns a string or Unicode representation of the parse tree + as an HTML or XML document. + :param pretty_print: If this is True, indentation will be used to + make the document more readable. + :param eventual_encoding: The encoding of the final document. + If this is None, the document will be a Unicode string. + """ if self.is_xml: # Print the XML declaration encoding_part = '' + if eventual_encoding in PYTHON_SPECIFIC_ENCODINGS: + # This is a special Python encoding; it can't actually + # go into an XML document because it means nothing + # outside of Python. + eventual_encoding = None if eventual_encoding != None: encoding_part = ' encoding="%s"' % eventual_encoding prefix = '\n' % encoding_part @@ -555,7 +777,7 @@ class BeautifulSoup(Tag): return prefix + super(BeautifulSoup, self).decode( indent_level, eventual_encoding, formatter) -# Alias to make it easier to type import: 'from bs4 import _soup' +# Aliases to make it easier to get started quickly, e.g. 'from bs4 import _soup' _s = BeautifulSoup _soup = BeautifulSoup @@ -566,19 +788,25 @@ class BeautifulStoneSoup(BeautifulSoup): kwargs['features'] = 'xml' warnings.warn( 'The BeautifulStoneSoup class is deprecated. Instead of using ' - 'it, pass features="xml" into the BeautifulSoup constructor.') + 'it, pass features="xml" into the BeautifulSoup constructor.', + DeprecationWarning + ) super(BeautifulStoneSoup, self).__init__(*args, **kwargs) class StopParsing(Exception): + """Exception raised by a TreeBuilder if it's unable to continue parsing.""" pass class FeatureNotFound(ValueError): + """Exception raised by the BeautifulSoup constructor if no parser with the + requested features is found. + """ pass -#By default, act as an HTML pretty-printer. +#If this file is run as a script, act as an HTML pretty-printer. if __name__ == '__main__': import sys soup = BeautifulSoup(sys.stdin) - print(soup.prettify()) + print((soup.prettify())) diff --git a/libs/common/bs4/builder/__init__.py b/libs/common/bs4/builder/__init__.py index b80ad684..9f789f3e 100644 --- a/libs/common/bs4/builder/__init__.py +++ b/libs/common/bs4/builder/__init__.py @@ -1,15 +1,21 @@ -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# Use of this source code is governed by the MIT license. +__license__ = "MIT" from collections import defaultdict import itertools +import re +import warnings import sys from bs4.element import ( CharsetMetaAttributeValue, ContentMetaAttributeValue, - HTMLAwareEntitySubstitution, - whitespace_re - ) + RubyParenthesisString, + RubyTextString, + Stylesheet, + Script, + TemplateString, + nonwhitespace_re +) __all__ = [ 'HTMLTreeBuilder', @@ -26,20 +32,41 @@ XML = 'xml' HTML = 'html' HTML_5 = 'html5' +class XMLParsedAsHTMLWarning(UserWarning): + """The warning issued when an HTML parser is used to parse + XML that is not XHTML. + """ + MESSAGE = """It looks like you're parsing an XML document using an HTML parser. If this really is an HTML document (maybe it's XHTML?), you can ignore or filter this warning. If it's XML, you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the lxml package installed, and pass the keyword argument `features="xml"` into the BeautifulSoup constructor.""" + class TreeBuilderRegistry(object): - + """A way of looking up TreeBuilder subclasses by their name or by desired + features. + """ + def __init__(self): self.builders_for_feature = defaultdict(list) self.builders = [] def register(self, treebuilder_class): - """Register a treebuilder based on its advertised features.""" + """Register a treebuilder based on its advertised features. + + :param treebuilder_class: A subclass of Treebuilder. its .features + attribute should list its features. + """ for feature in treebuilder_class.features: self.builders_for_feature[feature].insert(0, treebuilder_class) self.builders.insert(0, treebuilder_class) def lookup(self, *features): + """Look up a TreeBuilder subclass with the desired features. + + :param features: A list of features to look for. If none are + provided, the most recently registered TreeBuilder subclass + will be used. + :return: A TreeBuilder subclass, or None if there's no + registered subclass with all the requested features. + """ if len(self.builders) == 0: # There are no builders at all. return None @@ -82,7 +109,7 @@ class TreeBuilderRegistry(object): builder_registry = TreeBuilderRegistry() class TreeBuilder(object): - """Turn a document into a Beautiful Soup object tree.""" + """Turn a textual document into a Beautiful Soup object tree.""" NAME = "[Unknown tree builder]" ALTERNATE_NAMES = [] @@ -90,19 +117,89 @@ class TreeBuilder(object): is_xml = False picklable = False - preserve_whitespace_tags = set() empty_element_tags = None # A tag will be considered an empty-element # tag when and only when it has no contents. # A value for these tag/attribute combinations is a space- or # comma-separated list of CDATA, rather than a single CDATA. - cdata_list_attributes = {} + DEFAULT_CDATA_LIST_ATTRIBUTES = {} + # Whitespace should be preserved inside these tags. + DEFAULT_PRESERVE_WHITESPACE_TAGS = set() - def __init__(self): + # The textual contents of tags with these names should be + # instantiated with some class other than NavigableString. + DEFAULT_STRING_CONTAINERS = {} + + USE_DEFAULT = object() + + # Most parsers don't keep track of line numbers. + TRACKS_LINE_NUMBERS = False + + def __init__(self, multi_valued_attributes=USE_DEFAULT, + preserve_whitespace_tags=USE_DEFAULT, + store_line_numbers=USE_DEFAULT, + string_containers=USE_DEFAULT, + ): + """Constructor. + + :param multi_valued_attributes: If this is set to None, the + TreeBuilder will not turn any values for attributes like + 'class' into lists. Setting this to a dictionary will + customize this behavior; look at DEFAULT_CDATA_LIST_ATTRIBUTES + for an example. + + Internally, these are called "CDATA list attributes", but that + probably doesn't make sense to an end-user, so the argument name + is `multi_valued_attributes`. + + :param preserve_whitespace_tags: A list of tags to treat + the way

     tags are treated in HTML. Tags in this list
    +         are immune from pretty-printing; their contents will always be
    +         output as-is.
    +
    +        :param string_containers: A dictionary mapping tag names to
    +        the classes that should be instantiated to contain the textual
    +        contents of those tags. The default is to use NavigableString
    +        for every tag, no matter what the name. You can override the
    +        default by changing DEFAULT_STRING_CONTAINERS.
    +
    +        :param store_line_numbers: If the parser keeps track of the
    +         line numbers and positions of the original markup, that
    +         information will, by default, be stored in each corresponding
    +         `Tag` object. You can turn this off by passing
    +         store_line_numbers=False. If the parser you're using doesn't 
    +         keep track of this information, then setting store_line_numbers=True
    +         will do nothing.
    +        """
             self.soup = None
    +        if multi_valued_attributes is self.USE_DEFAULT:
    +            multi_valued_attributes = self.DEFAULT_CDATA_LIST_ATTRIBUTES
    +        self.cdata_list_attributes = multi_valued_attributes
    +        if preserve_whitespace_tags is self.USE_DEFAULT:
    +            preserve_whitespace_tags = self.DEFAULT_PRESERVE_WHITESPACE_TAGS
    +        self.preserve_whitespace_tags = preserve_whitespace_tags
    +        if store_line_numbers == self.USE_DEFAULT:
    +            store_line_numbers = self.TRACKS_LINE_NUMBERS
    +        self.store_line_numbers = store_line_numbers 
    +        if string_containers == self.USE_DEFAULT:
    +            string_containers = self.DEFAULT_STRING_CONTAINERS
    +        self.string_containers = string_containers
    +        
    +    def initialize_soup(self, soup):
    +        """The BeautifulSoup object has been initialized and is now
    +        being associated with the TreeBuilder.
     
    +        :param soup: A BeautifulSoup object.
    +        """
    +        self.soup = soup
    +        
         def reset(self):
    +        """Do any work necessary to reset the underlying parser
    +        for a new document.
    +
    +        By default, this does nothing.
    +        """
             pass
     
         def can_be_empty_element(self, tag_name):
    @@ -114,24 +211,58 @@ class TreeBuilder(object):
             For instance: an HTMLBuilder does not consider a 

    tag to be an empty-element tag (it's not in HTMLBuilder.empty_element_tags). This means an empty

    tag - will be presented as "

    ", not "

    ". + will be presented as "

    ", not "

    " or "

    ". The default implementation has no opinion about which tags are empty-element tags, so a tag will be presented as an - empty-element tag if and only if it has no contents. - "" will become "", and "bar" will + empty-element tag if and only if it has no children. + "" will become "", and "bar" will be left alone. + + :param tag_name: The name of a markup tag. """ if self.empty_element_tags is None: return True return tag_name in self.empty_element_tags - + def feed(self, markup): + """Run some incoming markup through some parsing process, + populating the `BeautifulSoup` object in self.soup. + + This method is not implemented in TreeBuilder; it must be + implemented in subclasses. + + :return: None. + """ raise NotImplementedError() def prepare_markup(self, markup, user_specified_encoding=None, - document_declared_encoding=None): - return markup, None, None, False + document_declared_encoding=None, exclude_encodings=None): + """Run any preliminary steps necessary to make incoming markup + acceptable to the parser. + + :param markup: Some markup -- probably a bytestring. + :param user_specified_encoding: The user asked to try this encoding. + :param document_declared_encoding: The markup itself claims to be + in this encoding. NOTE: This argument is not used by the + calling code and can probably be removed. + :param exclude_encodings: The user asked _not_ to try any of + these encodings. + + :yield: A series of 4-tuples: + (markup, encoding, declared encoding, + has undergone character replacement) + + Each 4-tuple represents a strategy for converting the + document to Unicode and parsing it. Each strategy will be tried + in turn. + + By default, the only strategy is to parse the markup + as-is. See `LXMLTreeBuilderForXML` and + `HTMLParserTreeBuilder` for implementations that take into + account the quirks of particular parsers. + """ + yield markup, None, None, False def test_fragment_to_document(self, fragment): """Wrap an HTML fragment to make it look like a document. @@ -143,16 +274,36 @@ class TreeBuilder(object): results against other HTML fragments. This method should not be used outside of tests. + + :param fragment: A string -- fragment of HTML. + :return: A string -- a full HTML document. """ return fragment def set_up_substitutions(self, tag): + """Set up any substitutions that will need to be performed on + a `Tag` when it's output as a string. + + By default, this does nothing. See `HTMLTreeBuilder` for a + case where this is used. + + :param tag: A `Tag` + :return: Whether or not a substitution was performed. + """ return False def _replace_cdata_list_attribute_values(self, tag_name, attrs): - """Replaces class="foo bar" with class=["foo", "bar"] + """When an attribute value is associated with a tag that can + have multiple values for that attribute, convert the string + value to a list of strings. - Modifies its input in place. + Basically, replaces class="foo bar" with class=["foo", "bar"] + + NOTE: This method modifies its input in place. + + :param tag_name: The name of a tag. + :param attrs: A dictionary containing the tag's attributes. + Any appropriate attribute values will be modified in place. """ if not attrs: return attrs @@ -167,7 +318,7 @@ class TreeBuilder(object): # values. Split it into a list. value = attrs[attr] if isinstance(value, str): - values = whitespace_re.split(value) + values = nonwhitespace_re.findall(value) else: # html5lib sometimes calls setAttributes twice # for the same tag when rearranging the parse @@ -178,9 +329,13 @@ class TreeBuilder(object): values = value attrs[attr] = values return attrs - + class SAXTreeBuilder(TreeBuilder): - """A Beautiful Soup treebuilder that listens for SAX events.""" + """A Beautiful Soup treebuilder that listens for SAX events. + + This is not currently used for anything, but it demonstrates + how a simple TreeBuilder would work. + """ def feed(self, markup): raise NotImplementedError() @@ -190,11 +345,11 @@ class SAXTreeBuilder(TreeBuilder): def startElement(self, name, attrs): attrs = dict((key[1], value) for key, value in list(attrs.items())) - #print "Start %s, %r" % (name, attrs) + #print("Start %s, %r" % (name, attrs)) self.soup.handle_starttag(name, attrs) def endElement(self, name): - #print "End %s" % name + #print("End %s" % name) self.soup.handle_endtag(name) def startElementNS(self, nsTuple, nodeName, attrs): @@ -231,7 +386,6 @@ class HTMLTreeBuilder(TreeBuilder): Such as which tags are empty-element tags. """ - preserve_whitespace_tags = HTMLAwareEntitySubstitution.preserve_whitespace_tags empty_element_tags = set([ # These are from HTML5. 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', @@ -245,6 +399,30 @@ class HTMLTreeBuilder(TreeBuilder): # but it may do so eventually, and this information is available if # you need to use it. block_elements = set(["address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hr", "li", "main", "nav", "noscript", "ol", "output", "p", "pre", "section", "table", "tfoot", "ul", "video"]) + + # These HTML tags need special treatment so they can be + # represented by a string class other than NavigableString. + # + # For some of these tags, it's because the HTML standard defines + # an unusual content model for them. I made this list by going + # through the HTML spec + # (https://html.spec.whatwg.org/#metadata-content) and looking for + # "metadata content" elements that can contain strings. + # + # The Ruby tags ( and ) are here despite being normal + # "phrasing content" tags, because the content they contain is + # qualitatively different from other text in the document, and it + # can be useful to be able to distinguish it. + # + # TODO: Arguably

    foobaz

    " - self.assertSoupEquals(markup) - - soup = self.soup(markup) - comment = soup.find(text="foobar") - self.assertEqual(comment.__class__, Comment) - - # The comment is properly integrated into the tree. - foo = soup.find(text="foo") - self.assertEqual(comment, foo.next_element) - baz = soup.find(text="baz") - self.assertEqual(comment, baz.previous_element) - - def test_preserved_whitespace_in_pre_and_textarea(self): - """Whitespace must be preserved in
     and "
    -        self.assertSoupEquals(pre_markup)
    -        self.assertSoupEquals(textarea_markup)
    -
    -        soup = self.soup(pre_markup)
    -        self.assertEqual(soup.pre.prettify(), pre_markup)
    -
    -        soup = self.soup(textarea_markup)
    -        self.assertEqual(soup.textarea.prettify(), textarea_markup)
    -
    -        soup = self.soup("")
    -        self.assertEqual(soup.textarea.prettify(), "")
    -
    -    def test_nested_inline_elements(self):
    -        """Inline elements can be nested indefinitely."""
    -        b_tag = "Inside a B tag"
    -        self.assertSoupEquals(b_tag)
    -
    -        nested_b_tag = "

    A nested tag

    " - self.assertSoupEquals(nested_b_tag) - - double_nested_b_tag = "

    A doubly nested tag

    " - self.assertSoupEquals(nested_b_tag) - - def test_nested_block_level_elements(self): - """Block elements can be nested.""" - soup = self.soup('

    Foo

    ') - blockquote = soup.blockquote - self.assertEqual(blockquote.p.b.string, 'Foo') - self.assertEqual(blockquote.b.string, 'Foo') - - def test_correctly_nested_tables(self): - """One table can go inside another one.""" - markup = ('' - '' - "') - - self.assertSoupEquals( - markup, - '
    Here's another table:" - '' - '' - '
    foo
    Here\'s another table:' - '
    foo
    ' - '
    ') - - self.assertSoupEquals( - "" - "" - "
    Foo
    Bar
    Baz
    ") - - def test_deeply_nested_multivalued_attribute(self): - # html5lib can set the attributes of the same tag many times - # as it rearranges the tree. This has caused problems with - # multivalued attributes. - markup = '
    ' - soup = self.soup(markup) - self.assertEqual(["css"], soup.div.div['class']) - - def test_multivalued_attribute_on_html(self): - # html5lib uses a different API to set the attributes ot the - # tag. This has caused problems with multivalued - # attributes. - markup = '' - soup = self.soup(markup) - self.assertEqual(["a", "b"], soup.html['class']) - - def test_angle_brackets_in_attribute_values_are_escaped(self): - self.assertSoupEquals('', '') - - def test_strings_resembling_character_entity_references(self): - # "&T" and "&p" look like incomplete character entities, but they are - # not. - self.assertSoupEquals( - "

    • AT&T is in the s&p 500

    ", - "

    \u2022 AT&T is in the s&p 500

    " - ) - - def test_entities_in_foreign_document_encoding(self): - # “ and ” are invalid numeric entities referencing - # Windows-1252 characters. - references a character common - # to Windows-1252 and Unicode, and ☃ references a - # character only found in Unicode. - # - # All of these entities should be converted to Unicode - # characters. - markup = "

    “Hello” -☃

    " - soup = self.soup(markup) - self.assertEqual("“Hello” -☃", soup.p.string) - - def test_entities_in_attributes_converted_to_unicode(self): - expect = '

    ' - self.assertSoupEquals('

    ', expect) - self.assertSoupEquals('

    ', expect) - self.assertSoupEquals('

    ', expect) - self.assertSoupEquals('

    ', expect) - - def test_entities_in_text_converted_to_unicode(self): - expect = '

    pi\N{LATIN SMALL LETTER N WITH TILDE}ata

    ' - self.assertSoupEquals("

    piñata

    ", expect) - self.assertSoupEquals("

    piñata

    ", expect) - self.assertSoupEquals("

    piñata

    ", expect) - self.assertSoupEquals("

    piñata

    ", expect) - - def test_quot_entity_converted_to_quotation_mark(self): - self.assertSoupEquals("

    I said "good day!"

    ", - '

    I said "good day!"

    ') - - def test_out_of_range_entity(self): - expect = "\N{REPLACEMENT CHARACTER}" - self.assertSoupEquals("�", expect) - self.assertSoupEquals("�", expect) - self.assertSoupEquals("�", expect) - - def test_multipart_strings(self): - "Mostly to prevent a recurrence of a bug in the html5lib treebuilder." - soup = self.soup("

    \nfoo

    ") - self.assertEqual("p", soup.h2.string.next_element.name) - self.assertEqual("p", soup.p.name) - self.assertConnectedness(soup) - - def test_empty_element_tags(self): - """Verify consistent handling of empty-element tags, - no matter how they come in through the markup. - """ - self.assertSoupEquals('


    ', "


    ") - self.assertSoupEquals('


    ', "


    ") - - def test_head_tag_between_head_and_body(self): - "Prevent recurrence of a bug in the html5lib treebuilder." - content = """ - - foo - -""" - soup = self.soup(content) - self.assertNotEqual(None, soup.html.body) - self.assertConnectedness(soup) - - def test_multiple_copies_of_a_tag(self): - "Prevent recurrence of a bug in the html5lib treebuilder." - content = """ - - - - - -""" - soup = self.soup(content) - self.assertConnectedness(soup.article) - - def test_basic_namespaces(self): - """Parsers don't need to *understand* namespaces, but at the - very least they should not choke on namespaces or lose - data.""" - - markup = b'4' - soup = self.soup(markup) - self.assertEqual(markup, soup.encode()) - html = soup.html - self.assertEqual('http://www.w3.org/1999/xhtml', soup.html['xmlns']) - self.assertEqual( - 'http://www.w3.org/1998/Math/MathML', soup.html['xmlns:mathml']) - self.assertEqual( - 'http://www.w3.org/2000/svg', soup.html['xmlns:svg']) - - def test_multivalued_attribute_value_becomes_list(self): - markup = b'' - soup = self.soup(markup) - self.assertEqual(['foo', 'bar'], soup.a['class']) - - # - # Generally speaking, tests below this point are more tests of - # Beautiful Soup than tests of the tree builders. But parsers are - # weird, so we run these tests separately for every tree builder - # to detect any differences between them. - # - - def test_can_parse_unicode_document(self): - # A seemingly innocuous document... but it's in Unicode! And - # it contains characters that can't be represented in the - # encoding found in the declaration! The horror! - markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' - soup = self.soup(markup) - self.assertEqual('Sacr\xe9 bleu!', soup.body.string) - - def test_soupstrainer(self): - """Parsers should be able to work with SoupStrainers.""" - strainer = SoupStrainer("b") - soup = self.soup("A bold statement", - parse_only=strainer) - self.assertEqual(soup.decode(), "bold") - - def test_single_quote_attribute_values_become_double_quotes(self): - self.assertSoupEquals("", - '') - - def test_attribute_values_with_nested_quotes_are_left_alone(self): - text = """a""" - self.assertSoupEquals(text) - - def test_attribute_values_with_double_nested_quotes_get_quoted(self): - text = """a""" - soup = self.soup(text) - soup.foo['attr'] = 'Brawls happen at "Bob\'s Bar"' - self.assertSoupEquals( - soup.foo.decode(), - """a""") - - def test_ampersand_in_attribute_value_gets_escaped(self): - self.assertSoupEquals('', - '') - - self.assertSoupEquals( - 'foo', - 'foo') - - def test_escaped_ampersand_in_attribute_value_is_left_alone(self): - self.assertSoupEquals('') - - def test_entities_in_strings_converted_during_parsing(self): - # Both XML and HTML entities are converted to Unicode characters - # during parsing. - text = "

    <<sacré bleu!>>

    " - expected = "

    <<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

    " - self.assertSoupEquals(text, expected) - - def test_smart_quotes_converted_on_the_way_in(self): - # Microsoft smart quotes are converted to Unicode characters during - # parsing. - quote = b"

    \x91Foo\x92

    " - soup = self.soup(quote) - self.assertEqual( - soup.p.string, - "\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}") - - def test_non_breaking_spaces_converted_on_the_way_in(self): - soup = self.soup("  ") - self.assertEqual(soup.a.string, "\N{NO-BREAK SPACE}" * 2) - - def test_entities_converted_on_the_way_out(self): - text = "

    <<sacré bleu!>>

    " - expected = "

    <<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

    ".encode("utf-8") - soup = self.soup(text) - self.assertEqual(soup.p.encode("utf-8"), expected) - - def test_real_iso_latin_document(self): - # Smoke test of interrelated functionality, using an - # easy-to-understand document. - - # Here it is in Unicode. Note that it claims to be in ISO-Latin-1. - unicode_html = '

    Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!

    ' - - # That's because we're going to encode it into ISO-Latin-1, and use - # that to test. - iso_latin_html = unicode_html.encode("iso-8859-1") - - # Parse the ISO-Latin-1 HTML. - soup = self.soup(iso_latin_html) - # Encode it to UTF-8. - result = soup.encode("utf-8") - - # What do we expect the result to look like? Well, it would - # look like unicode_html, except that the META tag would say - # UTF-8 instead of ISO-Latin-1. - expected = unicode_html.replace("ISO-Latin-1", "utf-8") - - # And, of course, it would be in UTF-8, not Unicode. - expected = expected.encode("utf-8") - - # Ta-da! - self.assertEqual(result, expected) - - def test_real_shift_jis_document(self): - # Smoke test to make sure the parser can handle a document in - # Shift-JIS encoding, without choking. - shift_jis_html = ( - b'
    '
    -            b'\x82\xb1\x82\xea\x82\xcdShift-JIS\x82\xc5\x83R\x81[\x83f'
    -            b'\x83B\x83\x93\x83O\x82\xb3\x82\xea\x82\xbd\x93\xfa\x96{\x8c'
    -            b'\xea\x82\xcc\x83t\x83@\x83C\x83\x8b\x82\xc5\x82\xb7\x81B'
    -            b'
    ') - unicode_html = shift_jis_html.decode("shift-jis") - soup = self.soup(unicode_html) - - # Make sure the parse tree is correctly encoded to various - # encodings. - self.assertEqual(soup.encode("utf-8"), unicode_html.encode("utf-8")) - self.assertEqual(soup.encode("euc_jp"), unicode_html.encode("euc_jp")) - - def test_real_hebrew_document(self): - # A real-world test to make sure we can convert ISO-8859-9 (a - # Hebrew encoding) to UTF-8. - hebrew_document = b'Hebrew (ISO 8859-8) in Visual Directionality

    Hebrew (ISO 8859-8) in Visual Directionality

    \xed\xe5\xec\xf9' - soup = self.soup( - hebrew_document, from_encoding="iso8859-8") - # Some tree builders call it iso8859-8, others call it iso-8859-9. - # That's not a difference we really care about. - assert soup.original_encoding in ('iso8859-8', 'iso-8859-8') - self.assertEqual( - soup.encode('utf-8'), - hebrew_document.decode("iso8859-8").encode("utf-8")) - - def test_meta_tag_reflects_current_encoding(self): - # Here's the tag saying that a document is - # encoded in Shift-JIS. - meta_tag = ('') - - # Here's a document incorporating that meta tag. - shift_jis_html = ( - '\n%s\n' - '' - 'Shift-JIS markup goes here.') % meta_tag - soup = self.soup(shift_jis_html) - - # Parse the document, and the charset is seemingly unaffected. - parsed_meta = soup.find('meta', {'http-equiv': 'Content-type'}) - content = parsed_meta['content'] - self.assertEqual('text/html; charset=x-sjis', content) - - # But that value is actually a ContentMetaAttributeValue object. - self.assertTrue(isinstance(content, ContentMetaAttributeValue)) - - # And it will take on a value that reflects its current - # encoding. - self.assertEqual('text/html; charset=utf8', content.encode("utf8")) - - # For the rest of the story, see TestSubstitutions in - # test_tree.py. - - def test_html5_style_meta_tag_reflects_current_encoding(self): - # Here's the tag saying that a document is - # encoded in Shift-JIS. - meta_tag = ('') - - # Here's a document incorporating that meta tag. - shift_jis_html = ( - '\n%s\n' - '' - 'Shift-JIS markup goes here.') % meta_tag - soup = self.soup(shift_jis_html) - - # Parse the document, and the charset is seemingly unaffected. - parsed_meta = soup.find('meta', id="encoding") - charset = parsed_meta['charset'] - self.assertEqual('x-sjis', charset) - - # But that value is actually a CharsetMetaAttributeValue object. - self.assertTrue(isinstance(charset, CharsetMetaAttributeValue)) - - # And it will take on a value that reflects its current - # encoding. - self.assertEqual('utf8', charset.encode("utf8")) - - def test_tag_with_no_attributes_can_have_attributes_added(self): - data = self.soup("text") - data.a['foo'] = 'bar' - self.assertEqual('text', data.a.decode()) - -class XMLTreeBuilderSmokeTest(object): - - def test_pickle_and_unpickle_identity(self): - # Pickling a tree, then unpickling it, yields a tree identical - # to the original. - tree = self.soup("foo") - dumped = pickle.dumps(tree, 2) - loaded = pickle.loads(dumped) - self.assertEqual(loaded.__class__, BeautifulSoup) - self.assertEqual(loaded.decode(), tree.decode()) - - def test_docstring_generated(self): - soup = self.soup("") - self.assertEqual( - soup.encode(), b'\n') - - def test_xml_declaration(self): - markup = b"""\n""" - soup = self.soup(markup) - self.assertEqual(markup, soup.encode("utf8")) - - def test_processing_instruction(self): - markup = b"""\n""" - soup = self.soup(markup) - self.assertEqual(markup, soup.encode("utf8")) - - def test_real_xhtml_document(self): - """A real XHTML document should come out *exactly* the same as it went in.""" - markup = b""" - - -Hello. -Goodbye. -""" - soup = self.soup(markup) - self.assertEqual( - soup.encode("utf-8"), markup) - - def test_nested_namespaces(self): - doc = b""" - - - - - -""" - soup = self.soup(doc) - self.assertEqual(doc, soup.encode()) - - def test_formatter_processes_script_tag_for_xml_documents(self): - doc = """ - -""" - soup = BeautifulSoup(doc, "lxml-xml") - # lxml would have stripped this while parsing, but we can add - # it later. - soup.script.string = 'console.log("< < hey > > ");' - encoded = soup.encode() - self.assertTrue(b"< < hey > >" in encoded) - - def test_can_parse_unicode_document(self): - markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' - soup = self.soup(markup) - self.assertEqual('Sacr\xe9 bleu!', soup.root.string) - - def test_popping_namespaced_tag(self): - markup = 'b2012-07-02T20:33:42Zcd' - soup = self.soup(markup) - self.assertEqual( - str(soup.rss), markup) - - def test_docstring_includes_correct_encoding(self): - soup = self.soup("") - self.assertEqual( - soup.encode("latin1"), - b'\n') - - def test_large_xml_document(self): - """A large XML document should come out the same as it went in.""" - markup = (b'\n' - + b'0' * (2**12) - + b'') - soup = self.soup(markup) - self.assertEqual(soup.encode("utf-8"), markup) - - - def test_tags_are_empty_element_if_and_only_if_they_are_empty(self): - self.assertSoupEquals("

    ", "

    ") - self.assertSoupEquals("

    foo

    ") - - def test_namespaces_are_preserved(self): - markup = 'This tag is in the a namespaceThis tag is in the b namespace' - soup = self.soup(markup) - root = soup.root - self.assertEqual("http://example.com/", root['xmlns:a']) - self.assertEqual("http://example.net/", root['xmlns:b']) - - def test_closing_namespaced_tag(self): - markup = '

    20010504

    ' - soup = self.soup(markup) - self.assertEqual(str(soup.p), markup) - - def test_namespaced_attributes(self): - markup = '' - soup = self.soup(markup) - self.assertEqual(str(soup.foo), markup) - - def test_namespaced_attributes_xml_namespace(self): - markup = 'bar' - soup = self.soup(markup) - self.assertEqual(str(soup.foo), markup) - - def test_find_by_prefixed_name(self): - doc = """ -foo - bar - baz - -""" - soup = self.soup(doc) - - # There are three tags. - self.assertEqual(3, len(soup.find_all('tag'))) - - # But two of them are ns1:tag and one of them is ns2:tag. - self.assertEqual(2, len(soup.find_all('ns1:tag'))) - self.assertEqual(1, len(soup.find_all('ns2:tag'))) - - self.assertEqual(1, len(soup.find_all('ns2:tag', key='value'))) - self.assertEqual(3, len(soup.find_all(['ns1:tag', 'ns2:tag']))) - - def test_copy_tag_preserves_namespace(self): - xml = """ -""" - - soup = self.soup(xml) - tag = soup.document - duplicate = copy.copy(tag) - - # The two tags have the same namespace prefix. - self.assertEqual(tag.prefix, duplicate.prefix) - - -class HTML5TreeBuilderSmokeTest(HTMLTreeBuilderSmokeTest): - """Smoke test for a tree builder that supports HTML5.""" - - def test_real_xhtml_document(self): - # Since XHTML is not HTML5, HTML5 parsers are not tested to handle - # XHTML documents in any particular way. - pass - - def test_html_tags_have_namespace(self): - markup = "" - soup = self.soup(markup) - self.assertEqual("http://www.w3.org/1999/xhtml", soup.a.namespace) - - def test_svg_tags_have_namespace(self): - markup = '' - soup = self.soup(markup) - namespace = "http://www.w3.org/2000/svg" - self.assertEqual(namespace, soup.svg.namespace) - self.assertEqual(namespace, soup.circle.namespace) - - - def test_mathml_tags_have_namespace(self): - markup = '5' - soup = self.soup(markup) - namespace = 'http://www.w3.org/1998/Math/MathML' - self.assertEqual(namespace, soup.math.namespace) - self.assertEqual(namespace, soup.msqrt.namespace) - - def test_xml_declaration_becomes_comment(self): - markup = '' - soup = self.soup(markup) - self.assertTrue(isinstance(soup.contents[0], Comment)) - self.assertEqual(soup.contents[0], '?xml version="1.0" encoding="utf-8"?') - self.assertEqual("html", soup.contents[0].next_element.name) - -def skipIf(condition, reason): - def nothing(test, *args, **kwargs): - return None - - def decorator(test_item): - if condition: - return nothing - else: - return test_item - - return decorator diff --git a/libs/common/bs4/tests/__init__.py b/libs/common/bs4/tests/__init__.py index 142c8cc3..4af4b0ce 100644 --- a/libs/common/bs4/tests/__init__.py +++ b/libs/common/bs4/tests/__init__.py @@ -1 +1,1191 @@ -"The beautifulsoup tests." +# encoding: utf-8 +"""Helper classes for tests.""" + +# Use of this source code is governed by the MIT license. +__license__ = "MIT" + +import pickle +import copy +import functools +import warnings +import pytest +from bs4 import BeautifulSoup +from bs4.element import ( + CharsetMetaAttributeValue, + Comment, + ContentMetaAttributeValue, + Doctype, + PYTHON_SPECIFIC_ENCODINGS, + SoupStrainer, + Script, + Stylesheet, + Tag +) + +from bs4.builder import ( + DetectsXMLParsedAsHTML, + HTMLParserTreeBuilder, + XMLParsedAsHTMLWarning, +) +default_builder = HTMLParserTreeBuilder + +BAD_DOCUMENT = """A bare string + + +
    +
    HTML5 does allow CDATA sections in SVG
    +
    A tag
    +
    A
    tag that supposedly has contents.
    +
    AT&T
    +
    +
    +
    This numeric entity is missing the final semicolon:
    +
    +
    a
    +
    This document contains (do you see it?)
    +
    This document ends with That attribute value was bogus
    +The doctype is invalid because it contains extra whitespace +
    That boolean attribute had no value
    +
    Here's a nonexistent entity: &#foo; (do you see it?)
    +
    This document ends before the entity finishes: > +

    Paragraphs shouldn't contain block display elements, but this one does:

    you see?

    +Multiple values for the same attribute. +
    Here's a table
    +
    +
    This tag contains nothing but whitespace:
    +

    This p tag is cut off by

    the end of the blockquote tag
    +
    Here's a nested table:
    foo
    This table contains bare markup
    + +
    This document contains a surprise doctype
    + +
    Tag name contains Unicode characters
    + + +""" + + +class SoupTest(object): + + @property + def default_builder(self): + return default_builder + + def soup(self, markup, **kwargs): + """Build a Beautiful Soup object from markup.""" + builder = kwargs.pop('builder', self.default_builder) + return BeautifulSoup(markup, builder=builder, **kwargs) + + def document_for(self, markup, **kwargs): + """Turn an HTML fragment into a document. + + The details depend on the builder. + """ + return self.default_builder(**kwargs).test_fragment_to_document(markup) + + def assert_soup(self, to_parse, compare_parsed_to=None): + """Parse some markup using Beautiful Soup and verify that + the output markup is as expected. + """ + builder = self.default_builder + obj = BeautifulSoup(to_parse, builder=builder) + if compare_parsed_to is None: + compare_parsed_to = to_parse + + # Verify that the documents come out the same. + assert obj.decode() == self.document_for(compare_parsed_to) + + # Also run some checks on the BeautifulSoup object itself: + + # Verify that every tag that was opened was eventually closed. + + # There are no tags in the open tag counter. + assert all(v==0 for v in list(obj.open_tag_counter.values())) + + # The only tag in the tag stack is the one for the root + # document. + assert [obj.ROOT_TAG_NAME] == [x.name for x in obj.tagStack] + + assertSoupEquals = assert_soup + + def assertConnectedness(self, element): + """Ensure that next_element and previous_element are properly + set for all descendants of the given element. + """ + earlier = None + for e in element.descendants: + if earlier: + assert e == earlier.next_element + assert earlier == e.previous_element + earlier = e + + def linkage_validator(self, el, _recursive_call=False): + """Ensure proper linkage throughout the document.""" + descendant = None + # Document element should have no previous element or previous sibling. + # It also shouldn't have a next sibling. + if el.parent is None: + assert el.previous_element is None,\ + "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + el, el.previous_element, None + ) + assert el.previous_sibling is None,\ + "Bad previous_sibling\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + el, el.previous_sibling, None + ) + assert el.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format( + el, el.next_sibling, None + ) + + idx = 0 + child = None + last_child = None + last_idx = len(el.contents) - 1 + for child in el.contents: + descendant = None + + # Parent should link next element to their first child + # That child should have no previous sibling + if idx == 0: + if el.parent is not None: + assert el.next_element is child,\ + "Bad next_element\nNODE: {}\nNEXT: {}\nEXPECTED: {}".format( + el, el.next_element, child + ) + assert child.previous_element is el,\ + "Bad previous_element\nNODE: {}\nPREV: {}\nEXPECTED: {}".format( + child, child.previous_element, el + ) + assert child.previous_sibling is None,\ + "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED: {}".format( + child, child.previous_sibling, None + ) + + # If not the first child, previous index should link as sibling to this index + # Previous element should match the last index or the last bubbled up descendant + else: + assert child.previous_sibling is el.contents[idx - 1],\ + "Bad previous_sibling\nNODE: {}\nPREV {}\nEXPECTED {}".format( + child, child.previous_sibling, el.contents[idx - 1] + ) + assert el.contents[idx - 1].next_sibling is child,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + el.contents[idx - 1], el.contents[idx - 1].next_sibling, child + ) + + if last_child is not None: + assert child.previous_element is last_child,\ + "Bad previous_element\nNODE: {}\nPREV {}\nEXPECTED {}\nCONTENTS {}".format( + child, child.previous_element, last_child, child.parent.contents + ) + assert last_child.next_element is child,\ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + last_child, last_child.next_element, child + ) + + if isinstance(child, Tag) and child.contents: + descendant = self.linkage_validator(child, True) + # A bubbled up descendant should have no next siblings + assert descendant.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + descendant, descendant.next_sibling, None + ) + + # Mark last child as either the bubbled up descendant or the current child + if descendant is not None: + last_child = descendant + else: + last_child = child + + # If last child, there are non next siblings + if idx == last_idx: + assert child.next_sibling is None,\ + "Bad next_sibling\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_sibling, None + ) + idx += 1 + + child = descendant if descendant is not None else child + if child is None: + child = el + + if not _recursive_call and child is not None: + target = el + while True: + if target is None: + assert child.next_element is None, \ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_element, None + ) + break + elif target.next_sibling is not None: + assert child.next_element is target.next_sibling, \ + "Bad next_element\nNODE: {}\nNEXT {}\nEXPECTED {}".format( + child, child.next_element, target.next_sibling + ) + break + target = target.parent + + # We are done, so nothing to return + return None + else: + # Return the child to the recursive caller + return child + + def assert_selects(self, tags, should_match): + """Make sure that the given tags have the correct text. + + This is used in tests that define a bunch of tags, each + containing a single string, and then select certain strings by + some mechanism. + """ + assert [tag.string for tag in tags] == should_match + + def assert_selects_ids(self, tags, should_match): + """Make sure that the given tags have the correct IDs. + + This is used in tests that define a bunch of tags, each + containing a single string, and then select certain strings by + some mechanism. + """ + assert [tag['id'] for tag in tags] == should_match + + +class TreeBuilderSmokeTest(object): + # Tests that are common to HTML and XML tree builders. + + @pytest.mark.parametrize( + "multi_valued_attributes", + [None, dict(b=['class']), {'*': ['notclass']}] + ) + def test_attribute_not_multi_valued(self, multi_valued_attributes): + markup = '' + soup = self.soup(markup, multi_valued_attributes=multi_valued_attributes) + assert soup.a['class'] == 'a b c' + + @pytest.mark.parametrize( + "multi_valued_attributes", [dict(a=['class']), {'*': ['class']}] + ) + def test_attribute_multi_valued(self, multi_valued_attributes): + markup = '' + soup = self.soup( + markup, multi_valued_attributes=multi_valued_attributes + ) + assert soup.a['class'] == ['a', 'b', 'c'] + + def test_fuzzed_input(self): + # This test centralizes in one place the various fuzz tests + # for Beautiful Soup created by the oss-fuzz project. + + # These strings superficially resemble markup, but they + # generally can't be parsed into anything. The best we can + # hope for is that parsing these strings won't crash the + # parser. + # + # n.b. This markup is commented out because these fuzz tests + # _do_ crash the parser. However the crashes are due to bugs + # in html.parser, not Beautiful Soup -- otherwise I'd fix the + # bugs! + + bad_markup = [ + # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=28873 + # https://github.com/guidovranken/python-library-fuzzers/blob/master/corp-html/519e5b4269a01185a0d5e76295251921da2f0700 + # https://bugs.python.org/issue37747 + # + #b'\nSome CSS" + ) + assert isinstance(soup.style.string, Stylesheet) + assert isinstance(soup.script.string, Script) + + soup = self.soup( + "" + ) + assert isinstance(soup.style.string, Stylesheet) + # The contents of the style tag resemble an HTML comment, but + # it's not treated as a comment. + assert soup.style.string == "" + assert isinstance(soup.style.string, Stylesheet) + + def test_pickle_and_unpickle_identity(self): + # Pickling a tree, then unpickling it, yields a tree identical + # to the original. + tree = self.soup("foo") + dumped = pickle.dumps(tree, 2) + loaded = pickle.loads(dumped) + assert loaded.__class__ == BeautifulSoup + assert loaded.decode() == tree.decode() + + def assertDoctypeHandled(self, doctype_fragment): + """Assert that a given doctype string is handled correctly.""" + doctype_str, soup = self._document_with_doctype(doctype_fragment) + + # Make sure a Doctype object was created. + doctype = soup.contents[0] + assert doctype.__class__ == Doctype + assert doctype == doctype_fragment + assert soup.encode("utf8")[:len(doctype_str)] == doctype_str + + # Make sure that the doctype was correctly associated with the + # parse tree and that the rest of the document parsed. + assert soup.p.contents[0] == 'foo' + + def _document_with_doctype(self, doctype_fragment, doctype_string="DOCTYPE"): + """Generate and parse a document with the given doctype.""" + doctype = '' % (doctype_string, doctype_fragment) + markup = doctype + '\n

    foo

    ' + soup = self.soup(markup) + return doctype.encode("utf8"), soup + + def test_normal_doctypes(self): + """Make sure normal, everyday HTML doctypes are handled correctly.""" + self.assertDoctypeHandled("html") + self.assertDoctypeHandled( + 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"') + + def test_empty_doctype(self): + soup = self.soup("") + doctype = soup.contents[0] + assert "" == doctype.strip() + + def test_mixed_case_doctype(self): + # A lowercase or mixed-case doctype becomes a Doctype. + for doctype_fragment in ("doctype", "DocType"): + doctype_str, soup = self._document_with_doctype( + "html", doctype_fragment + ) + + # Make sure a Doctype object was created and that the DOCTYPE + # is uppercase. + doctype = soup.contents[0] + assert doctype.__class__ == Doctype + assert doctype == "html" + assert soup.encode("utf8")[:len(doctype_str)] == b"" + + # Make sure that the doctype was correctly associated with the + # parse tree and that the rest of the document parsed. + assert soup.p.contents[0] == 'foo' + + def test_public_doctype_with_url(self): + doctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"' + self.assertDoctypeHandled(doctype) + + def test_system_doctype(self): + self.assertDoctypeHandled('foo SYSTEM "http://www.example.com/"') + + def test_namespaced_system_doctype(self): + # We can handle a namespaced doctype with a system ID. + self.assertDoctypeHandled('xsl:stylesheet SYSTEM "htmlent.dtd"') + + def test_namespaced_public_doctype(self): + # Test a namespaced doctype with a public id. + self.assertDoctypeHandled('xsl:stylesheet PUBLIC "htmlent.dtd"') + + def test_real_xhtml_document(self): + """A real XHTML document should come out more or less the same as it went in.""" + markup = b""" + + +Hello. +Goodbye. +""" + with warnings.catch_warnings(record=True) as w: + soup = self.soup(markup) + assert soup.encode("utf-8").replace(b"\n", b"") == markup.replace(b"\n", b"") + + # No warning was issued about parsing an XML document as HTML, + # because XHTML is both. + assert w == [] + + + def test_namespaced_html(self): + # When a namespaced XML document is parsed as HTML it should + # be treated as HTML with weird tag names. + markup = b"""content""" + with warnings.catch_warnings(record=True) as w: + soup = self.soup(markup) + + assert 2 == len(soup.find_all("ns1:foo")) + + # n.b. no "you're parsing XML as HTML" warning was given + # because there was no XML declaration. + assert [] == w + + def test_detect_xml_parsed_as_html(self): + # A warning is issued when parsing an XML document as HTML, + # but basic stuff should still work. + markup = b"""string""" + with warnings.catch_warnings(record=True) as w: + soup = self.soup(markup) + assert soup.tag.string == 'string' + [warning] = w + assert isinstance(warning.message, XMLParsedAsHTMLWarning) + assert str(warning.message) == XMLParsedAsHTMLWarning.MESSAGE + + # NOTE: the warning is not issued if the document appears to + # be XHTML (tested with test_real_xhtml_document in the + # superclass) or if there is no XML declaration (tested with + # test_namespaced_html in the superclass). + + def test_processing_instruction(self): + # We test both Unicode and bytestring to verify that + # process_markup correctly sets processing_instruction_class + # even when the markup is already Unicode and there is no + # need to process anything. + markup = """""" + soup = self.soup(markup) + assert markup == soup.decode() + + markup = b"""""" + soup = self.soup(markup) + assert markup == soup.encode("utf8") + + def test_deepcopy(self): + """Make sure you can copy the tree builder. + + This is important because the builder is part of a + BeautifulSoup object, and we want to be able to copy that. + """ + copy.deepcopy(self.default_builder) + + def test_p_tag_is_never_empty_element(self): + """A

    tag is never designated as an empty-element tag. + + Even if the markup shows it as an empty-element tag, it + shouldn't be presented that way. + """ + soup = self.soup("

    ") + assert not soup.p.is_empty_element + assert str(soup.p) == "

    " + + def test_unclosed_tags_get_closed(self): + """A tag that's not closed by the end of the document should be closed. + + This applies to all tags except empty-element tags. + """ + self.assert_soup("

    ", "

    ") + self.assert_soup("", "") + + self.assert_soup("
    ", "
    ") + + def test_br_is_always_empty_element_tag(self): + """A
    tag is designated as an empty-element tag. + + Some parsers treat

    as one
    tag, some parsers as + two tags, but it should always be an empty-element tag. + """ + soup = self.soup("

    ") + assert soup.br.is_empty_element + assert str(soup.br) == "
    " + + def test_nested_formatting_elements(self): + self.assert_soup("") + + def test_double_head(self): + html = ''' + + +Ordinary HEAD element test + + + +Hello, world! + + +''' + soup = self.soup(html) + assert "text/javascript" == soup.find('script')['type'] + + def test_comment(self): + # Comments are represented as Comment objects. + markup = "

    foobaz

    " + self.assert_soup(markup) + + soup = self.soup(markup) + comment = soup.find(string="foobar") + assert comment.__class__ == Comment + + # The comment is properly integrated into the tree. + foo = soup.find(string="foo") + assert comment == foo.next_element + baz = soup.find(string="baz") + assert comment == baz.previous_element + + def test_preserved_whitespace_in_pre_and_textarea(self): + """Whitespace must be preserved in
     and "
    +        self.assert_soup(pre_markup)
    +        self.assert_soup(textarea_markup)
    +
    +        soup = self.soup(pre_markup)
    +        assert soup.pre.prettify() == pre_markup
    +
    +        soup = self.soup(textarea_markup)
    +        assert soup.textarea.prettify() == textarea_markup
    +
    +        soup = self.soup("")
    +        assert soup.textarea.prettify() == ""
    +
    +    def test_nested_inline_elements(self):
    +        """Inline elements can be nested indefinitely."""
    +        b_tag = "Inside a B tag"
    +        self.assert_soup(b_tag)
    +
    +        nested_b_tag = "

    A nested tag

    " + self.assert_soup(nested_b_tag) + + double_nested_b_tag = "

    A doubly nested tag

    " + self.assert_soup(nested_b_tag) + + def test_nested_block_level_elements(self): + """Block elements can be nested.""" + soup = self.soup('

    Foo

    ') + blockquote = soup.blockquote + assert blockquote.p.b.string == 'Foo' + assert blockquote.b.string == 'Foo' + + def test_correctly_nested_tables(self): + """One table can go inside another one.""" + markup = ('' + '' + "') + + self.assert_soup( + markup, + '
    Here's another table:" + '' + '' + '
    foo
    Here\'s another table:' + '
    foo
    ' + '
    ') + + self.assert_soup( + "" + "" + "
    Foo
    Bar
    Baz
    ") + + def test_multivalued_attribute_with_whitespace(self): + # Whitespace separating the values of a multi-valued attribute + # should be ignored. + + markup = '
    ' + soup = self.soup(markup) + assert ['foo', 'bar'] == soup.div['class'] + + # If you search by the literal name of the class it's like the whitespace + # wasn't there. + assert soup.div == soup.find('div', class_="foo bar") + + def test_deeply_nested_multivalued_attribute(self): + # html5lib can set the attributes of the same tag many times + # as it rearranges the tree. This has caused problems with + # multivalued attributes. + markup = '
    ' + soup = self.soup(markup) + assert ["css"] == soup.div.div['class'] + + def test_multivalued_attribute_on_html(self): + # html5lib uses a different API to set the attributes ot the + # tag. This has caused problems with multivalued + # attributes. + markup = '' + soup = self.soup(markup) + assert ["a", "b"] == soup.html['class'] + + def test_angle_brackets_in_attribute_values_are_escaped(self): + self.assert_soup('', '') + + def test_strings_resembling_character_entity_references(self): + # "&T" and "&p" look like incomplete character entities, but they are + # not. + self.assert_soup( + "

    • AT&T is in the s&p 500

    ", + "

    \u2022 AT&T is in the s&p 500

    " + ) + + def test_apos_entity(self): + self.assert_soup( + "

    Bob's Bar

    ", + "

    Bob's Bar

    ", + ) + + def test_entities_in_foreign_document_encoding(self): + # “ and ” are invalid numeric entities referencing + # Windows-1252 characters. - references a character common + # to Windows-1252 and Unicode, and ☃ references a + # character only found in Unicode. + # + # All of these entities should be converted to Unicode + # characters. + markup = "

    “Hello” -☃

    " + soup = self.soup(markup) + assert "“Hello” -☃" == soup.p.string + + def test_entities_in_attributes_converted_to_unicode(self): + expect = '

    ' + self.assert_soup('

    ', expect) + self.assert_soup('

    ', expect) + self.assert_soup('

    ', expect) + self.assert_soup('

    ', expect) + + def test_entities_in_text_converted_to_unicode(self): + expect = '

    pi\N{LATIN SMALL LETTER N WITH TILDE}ata

    ' + self.assert_soup("

    piñata

    ", expect) + self.assert_soup("

    piñata

    ", expect) + self.assert_soup("

    piñata

    ", expect) + self.assert_soup("

    piñata

    ", expect) + + def test_quot_entity_converted_to_quotation_mark(self): + self.assert_soup("

    I said "good day!"

    ", + '

    I said "good day!"

    ') + + def test_out_of_range_entity(self): + expect = "\N{REPLACEMENT CHARACTER}" + self.assert_soup("�", expect) + self.assert_soup("�", expect) + self.assert_soup("�", expect) + + def test_multipart_strings(self): + "Mostly to prevent a recurrence of a bug in the html5lib treebuilder." + soup = self.soup("

    \nfoo

    ") + assert "p" == soup.h2.string.next_element.name + assert "p" == soup.p.name + self.assertConnectedness(soup) + + def test_empty_element_tags(self): + """Verify consistent handling of empty-element tags, + no matter how they come in through the markup. + """ + self.assert_soup('


    ', "


    ") + self.assert_soup('


    ', "


    ") + + def test_head_tag_between_head_and_body(self): + "Prevent recurrence of a bug in the html5lib treebuilder." + content = """ + + foo + +""" + soup = self.soup(content) + assert soup.html.body is not None + self.assertConnectedness(soup) + + def test_multiple_copies_of_a_tag(self): + "Prevent recurrence of a bug in the html5lib treebuilder." + content = """ + + + + + +""" + soup = self.soup(content) + self.assertConnectedness(soup.article) + + def test_basic_namespaces(self): + """Parsers don't need to *understand* namespaces, but at the + very least they should not choke on namespaces or lose + data.""" + + markup = b'4' + soup = self.soup(markup) + assert markup == soup.encode() + html = soup.html + assert 'http://www.w3.org/1999/xhtml' == soup.html['xmlns'] + assert 'http://www.w3.org/1998/Math/MathML' == soup.html['xmlns:mathml'] + assert 'http://www.w3.org/2000/svg' == soup.html['xmlns:svg'] + + def test_multivalued_attribute_value_becomes_list(self): + markup = b'' + soup = self.soup(markup) + assert ['foo', 'bar'] == soup.a['class'] + + # + # Generally speaking, tests below this point are more tests of + # Beautiful Soup than tests of the tree builders. But parsers are + # weird, so we run these tests separately for every tree builder + # to detect any differences between them. + # + + def test_can_parse_unicode_document(self): + # A seemingly innocuous document... but it's in Unicode! And + # it contains characters that can't be represented in the + # encoding found in the declaration! The horror! + markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' + soup = self.soup(markup) + assert 'Sacr\xe9 bleu!' == soup.body.string + + def test_soupstrainer(self): + """Parsers should be able to work with SoupStrainers.""" + strainer = SoupStrainer("b") + soup = self.soup("A bold statement", + parse_only=strainer) + assert soup.decode() == "bold" + + def test_single_quote_attribute_values_become_double_quotes(self): + self.assert_soup("", + '') + + def test_attribute_values_with_nested_quotes_are_left_alone(self): + text = """a""" + self.assert_soup(text) + + def test_attribute_values_with_double_nested_quotes_get_quoted(self): + text = """a""" + soup = self.soup(text) + soup.foo['attr'] = 'Brawls happen at "Bob\'s Bar"' + self.assert_soup( + soup.foo.decode(), + """a""") + + def test_ampersand_in_attribute_value_gets_escaped(self): + self.assert_soup('', + '') + + self.assert_soup( + 'foo', + 'foo') + + def test_escaped_ampersand_in_attribute_value_is_left_alone(self): + self.assert_soup('') + + def test_entities_in_strings_converted_during_parsing(self): + # Both XML and HTML entities are converted to Unicode characters + # during parsing. + text = "

    <<sacré bleu!>>

    " + expected = "

    <<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

    " + self.assert_soup(text, expected) + + def test_smart_quotes_converted_on_the_way_in(self): + # Microsoft smart quotes are converted to Unicode characters during + # parsing. + quote = b"

    \x91Foo\x92

    " + soup = self.soup(quote) + assert soup.p.string == "\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}" + + def test_non_breaking_spaces_converted_on_the_way_in(self): + soup = self.soup("  ") + assert soup.a.string == "\N{NO-BREAK SPACE}" * 2 + + def test_entities_converted_on_the_way_out(self): + text = "

    <<sacré bleu!>>

    " + expected = "

    <<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>>

    ".encode("utf-8") + soup = self.soup(text) + assert soup.p.encode("utf-8") == expected + + def test_real_iso_latin_document(self): + # Smoke test of interrelated functionality, using an + # easy-to-understand document. + + # Here it is in Unicode. Note that it claims to be in ISO-Latin-1. + unicode_html = '

    Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!

    ' + + # That's because we're going to encode it into ISO-Latin-1, and use + # that to test. + iso_latin_html = unicode_html.encode("iso-8859-1") + + # Parse the ISO-Latin-1 HTML. + soup = self.soup(iso_latin_html) + # Encode it to UTF-8. + result = soup.encode("utf-8") + + # What do we expect the result to look like? Well, it would + # look like unicode_html, except that the META tag would say + # UTF-8 instead of ISO-Latin-1. + expected = unicode_html.replace("ISO-Latin-1", "utf-8") + + # And, of course, it would be in UTF-8, not Unicode. + expected = expected.encode("utf-8") + + # Ta-da! + assert result == expected + + def test_real_shift_jis_document(self): + # Smoke test to make sure the parser can handle a document in + # Shift-JIS encoding, without choking. + shift_jis_html = ( + b'
    '
    +            b'\x82\xb1\x82\xea\x82\xcdShift-JIS\x82\xc5\x83R\x81[\x83f'
    +            b'\x83B\x83\x93\x83O\x82\xb3\x82\xea\x82\xbd\x93\xfa\x96{\x8c'
    +            b'\xea\x82\xcc\x83t\x83@\x83C\x83\x8b\x82\xc5\x82\xb7\x81B'
    +            b'
    ') + unicode_html = shift_jis_html.decode("shift-jis") + soup = self.soup(unicode_html) + + # Make sure the parse tree is correctly encoded to various + # encodings. + assert soup.encode("utf-8") == unicode_html.encode("utf-8") + assert soup.encode("euc_jp") == unicode_html.encode("euc_jp") + + def test_real_hebrew_document(self): + # A real-world test to make sure we can convert ISO-8859-9 (a + # Hebrew encoding) to UTF-8. + hebrew_document = b'Hebrew (ISO 8859-8) in Visual Directionality

    Hebrew (ISO 8859-8) in Visual Directionality

    \xed\xe5\xec\xf9' + soup = self.soup( + hebrew_document, from_encoding="iso8859-8") + # Some tree builders call it iso8859-8, others call it iso-8859-9. + # That's not a difference we really care about. + assert soup.original_encoding in ('iso8859-8', 'iso-8859-8') + assert soup.encode('utf-8') == ( + hebrew_document.decode("iso8859-8").encode("utf-8") + ) + + def test_meta_tag_reflects_current_encoding(self): + # Here's the tag saying that a document is + # encoded in Shift-JIS. + meta_tag = ('') + + # Here's a document incorporating that meta tag. + shift_jis_html = ( + '\n%s\n' + '' + 'Shift-JIS markup goes here.') % meta_tag + soup = self.soup(shift_jis_html) + + # Parse the document, and the charset is seemingly unaffected. + parsed_meta = soup.find('meta', {'http-equiv': 'Content-type'}) + content = parsed_meta['content'] + assert 'text/html; charset=x-sjis' == content + + # But that value is actually a ContentMetaAttributeValue object. + assert isinstance(content, ContentMetaAttributeValue) + + # And it will take on a value that reflects its current + # encoding. + assert 'text/html; charset=utf8' == content.encode("utf8") + + # For the rest of the story, see TestSubstitutions in + # test_tree.py. + + def test_html5_style_meta_tag_reflects_current_encoding(self): + # Here's the tag saying that a document is + # encoded in Shift-JIS. + meta_tag = ('') + + # Here's a document incorporating that meta tag. + shift_jis_html = ( + '\n%s\n' + '' + 'Shift-JIS markup goes here.') % meta_tag + soup = self.soup(shift_jis_html) + + # Parse the document, and the charset is seemingly unaffected. + parsed_meta = soup.find('meta', id="encoding") + charset = parsed_meta['charset'] + assert 'x-sjis' == charset + + # But that value is actually a CharsetMetaAttributeValue object. + assert isinstance(charset, CharsetMetaAttributeValue) + + # And it will take on a value that reflects its current + # encoding. + assert 'utf8' == charset.encode("utf8") + + def test_python_specific_encodings_not_used_in_charset(self): + # You can encode an HTML document using a Python-specific + # encoding, but that encoding won't be mentioned _inside_ the + # resulting document. Instead, the document will appear to + # have no encoding. + for markup in [ + b'' + b'' + ]: + soup = self.soup(markup) + for encoding in PYTHON_SPECIFIC_ENCODINGS: + if encoding in ( + 'idna', 'mbcs', 'oem', 'undefined', + 'string_escape', 'string-escape' + ): + # For one reason or another, these will raise an + # exception if we actually try to use them, so don't + # bother. + continue + encoded = soup.encode(encoding) + assert b'meta charset=""' in encoded + assert encoding.encode("ascii") not in encoded + + def test_tag_with_no_attributes_can_have_attributes_added(self): + data = self.soup("text") + data.a['foo'] = 'bar' + assert 'text' == data.a.decode() + + def test_closing_tag_with_no_opening_tag(self): + # Without BeautifulSoup.open_tag_counter, the tag will + # cause _popToTag to be called over and over again as we look + # for a tag that wasn't there. The result is that 'text2' + # will show up outside the body of the document. + soup = self.soup("

    text1

    text2
    ") + assert "

    text1

    text2
    " == soup.body.decode() + + def test_worst_case(self): + """Test the worst case (currently) for linking issues.""" + + soup = self.soup(BAD_DOCUMENT) + self.linkage_validator(soup) + + +class XMLTreeBuilderSmokeTest(TreeBuilderSmokeTest): + + def test_pickle_and_unpickle_identity(self): + # Pickling a tree, then unpickling it, yields a tree identical + # to the original. + tree = self.soup("foo") + dumped = pickle.dumps(tree, 2) + loaded = pickle.loads(dumped) + assert loaded.__class__ == BeautifulSoup + assert loaded.decode() == tree.decode() + + def test_docstring_generated(self): + soup = self.soup("") + assert soup.encode() == b'\n' + + def test_xml_declaration(self): + markup = b"""\n""" + soup = self.soup(markup) + assert markup == soup.encode("utf8") + + def test_python_specific_encodings_not_used_in_xml_declaration(self): + # You can encode an XML document using a Python-specific + # encoding, but that encoding won't be mentioned _inside_ the + # resulting document. + markup = b"""\n""" + soup = self.soup(markup) + for encoding in PYTHON_SPECIFIC_ENCODINGS: + if encoding in ( + 'idna', 'mbcs', 'oem', 'undefined', + 'string_escape', 'string-escape' + ): + # For one reason or another, these will raise an + # exception if we actually try to use them, so don't + # bother. + continue + encoded = soup.encode(encoding) + assert b'' in encoded + assert encoding.encode("ascii") not in encoded + + def test_processing_instruction(self): + markup = b"""\n""" + soup = self.soup(markup) + assert markup == soup.encode("utf8") + + def test_real_xhtml_document(self): + """A real XHTML document should come out *exactly* the same as it went in.""" + markup = b""" + + +Hello. +Goodbye. +""" + soup = self.soup(markup) + assert soup.encode("utf-8") == markup + + def test_nested_namespaces(self): + doc = b""" + + + + + +""" + soup = self.soup(doc) + assert doc == soup.encode() + + def test_formatter_processes_script_tag_for_xml_documents(self): + doc = """ + +""" + soup = BeautifulSoup(doc, "lxml-xml") + # lxml would have stripped this while parsing, but we can add + # it later. + soup.script.string = 'console.log("< < hey > > ");' + encoded = soup.encode() + assert b"< < hey > >" in encoded + + def test_can_parse_unicode_document(self): + markup = 'Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' + soup = self.soup(markup) + assert 'Sacr\xe9 bleu!' == soup.root.string + + def test_can_parse_unicode_document_begining_with_bom(self): + markup = '\N{BYTE ORDER MARK}Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!' + soup = self.soup(markup) + assert 'Sacr\xe9 bleu!' == soup.root.string + + def test_popping_namespaced_tag(self): + markup = 'b2012-07-02T20:33:42Zcd' + soup = self.soup(markup) + assert str(soup.rss) == markup + + def test_docstring_includes_correct_encoding(self): + soup = self.soup("") + assert soup.encode("latin1") == b'\n' + + def test_large_xml_document(self): + """A large XML document should come out the same as it went in.""" + markup = (b'\n' + + b'0' * (2**12) + + b'') + soup = self.soup(markup) + assert soup.encode("utf-8") == markup + + def test_tags_are_empty_element_if_and_only_if_they_are_empty(self): + self.assert_soup("

    ", "

    ") + self.assert_soup("

    foo

    ") + + def test_namespaces_are_preserved(self): + markup = 'This tag is in the a namespaceThis tag is in the b namespace' + soup = self.soup(markup) + root = soup.root + assert "http://example.com/" == root['xmlns:a'] + assert "http://example.net/" == root['xmlns:b'] + + def test_closing_namespaced_tag(self): + markup = '

    20010504

    ' + soup = self.soup(markup) + assert str(soup.p) == markup + + def test_namespaced_attributes(self): + markup = '' + soup = self.soup(markup) + assert str(soup.foo) == markup + + def test_namespaced_attributes_xml_namespace(self): + markup = 'bar' + soup = self.soup(markup) + assert str(soup.foo) == markup + + def test_find_by_prefixed_name(self): + doc = """ +foo + bar + baz + +""" + soup = self.soup(doc) + + # There are three tags. + assert 3 == len(soup.find_all('tag')) + + # But two of them are ns1:tag and one of them is ns2:tag. + assert 2 == len(soup.find_all('ns1:tag')) + assert 1 == len(soup.find_all('ns2:tag')) + + assert 1, len(soup.find_all('ns2:tag', key='value')) + assert 3, len(soup.find_all(['ns1:tag', 'ns2:tag'])) + + def test_copy_tag_preserves_namespace(self): + xml = """ +""" + + soup = self.soup(xml) + tag = soup.document + duplicate = copy.copy(tag) + + # The two tags have the same namespace prefix. + assert tag.prefix == duplicate.prefix + + def test_worst_case(self): + """Test the worst case (currently) for linking issues.""" + + soup = self.soup(BAD_DOCUMENT) + self.linkage_validator(soup) + + +class HTML5TreeBuilderSmokeTest(HTMLTreeBuilderSmokeTest): + """Smoke test for a tree builder that supports HTML5.""" + + def test_real_xhtml_document(self): + # Since XHTML is not HTML5, HTML5 parsers are not tested to handle + # XHTML documents in any particular way. + pass + + def test_html_tags_have_namespace(self): + markup = "" + soup = self.soup(markup) + assert "http://www.w3.org/1999/xhtml" == soup.a.namespace + + def test_svg_tags_have_namespace(self): + markup = '' + soup = self.soup(markup) + namespace = "http://www.w3.org/2000/svg" + assert namespace == soup.svg.namespace + assert namespace == soup.circle.namespace + + + def test_mathml_tags_have_namespace(self): + markup = '5' + soup = self.soup(markup) + namespace = 'http://www.w3.org/1998/Math/MathML' + assert namespace == soup.math.namespace + assert namespace == soup.msqrt.namespace + + def test_xml_declaration_becomes_comment(self): + markup = '' + soup = self.soup(markup) + assert isinstance(soup.contents[0], Comment) + assert soup.contents[0] == '?xml version="1.0" encoding="utf-8"?' + assert "html" == soup.contents[0].next_element.name + +def skipIf(condition, reason): + def nothing(test, *args, **kwargs): + return None + + def decorator(test_item): + if condition: + return nothing + else: + return test_item + + return decorator diff --git a/libs/common/bs4/tests/test_builder.py b/libs/common/bs4/tests/test_builder.py new file mode 100644 index 00000000..75370712 --- /dev/null +++ b/libs/common/bs4/tests/test_builder.py @@ -0,0 +1,29 @@ +import pytest +from unittest.mock import patch +from bs4.builder import DetectsXMLParsedAsHTML + +class TestDetectsXMLParsedAsHTML(object): + + @pytest.mark.parametrize( + "markup,looks_like_xml", + [("No xml declaration", False), + ("obviously HTMLActually XHTML", False), + (" < html>Tricky XHTML", False), + ("", True), + ] + ) + def test_warn_if_markup_looks_like_xml(self, markup, looks_like_xml): + # Test of our ability to guess at whether markup looks XML-ish + # _and_ not HTML-ish. + with patch('bs4.builder.DetectsXMLParsedAsHTML._warn') as mock: + for data in markup, markup.encode('utf8'): + result = DetectsXMLParsedAsHTML.warn_if_markup_looks_like_xml( + data + ) + assert result == looks_like_xml + if looks_like_xml: + assert mock.called + else: + assert not mock.called + mock.reset_mock() diff --git a/libs/common/bs4/tests/test_builder_registry.py b/libs/common/bs4/tests/test_builder_registry.py index 90cad829..5fa874c8 100644 --- a/libs/common/bs4/tests/test_builder_registry.py +++ b/libs/common/bs4/tests/test_builder_registry.py @@ -1,6 +1,6 @@ """Tests of the builder registry.""" -import unittest +import pytest import warnings from bs4 import BeautifulSoup @@ -26,46 +26,36 @@ except ImportError: LXML_PRESENT = False -class BuiltInRegistryTest(unittest.TestCase): +class TestBuiltInRegistry(object): """Test the built-in registry with the default builders registered.""" def test_combination(self): + assert registry.lookup('strict', 'html') == HTMLParserTreeBuilder if LXML_PRESENT: - self.assertEqual(registry.lookup('fast', 'html'), - LXMLTreeBuilder) - - if LXML_PRESENT: - self.assertEqual(registry.lookup('permissive', 'xml'), - LXMLTreeBuilderForXML) - self.assertEqual(registry.lookup('strict', 'html'), - HTMLParserTreeBuilder) + assert registry.lookup('fast', 'html') == LXMLTreeBuilder + assert registry.lookup('permissive', 'xml') == LXMLTreeBuilderForXML if HTML5LIB_PRESENT: - self.assertEqual(registry.lookup('html5lib', 'html'), - HTML5TreeBuilder) + assert registry.lookup('html5lib', 'html') == HTML5TreeBuilder def test_lookup_by_markup_type(self): if LXML_PRESENT: - self.assertEqual(registry.lookup('html'), LXMLTreeBuilder) - self.assertEqual(registry.lookup('xml'), LXMLTreeBuilderForXML) + assert registry.lookup('html') == LXMLTreeBuilder + assert registry.lookup('xml') == LXMLTreeBuilderForXML else: - self.assertEqual(registry.lookup('xml'), None) + assert registry.lookup('xml') == None if HTML5LIB_PRESENT: - self.assertEqual(registry.lookup('html'), HTML5TreeBuilder) + assert registry.lookup('html') == HTML5TreeBuilder else: - self.assertEqual(registry.lookup('html'), HTMLParserTreeBuilder) + assert registry.lookup('html') == HTMLParserTreeBuilder def test_named_library(self): if LXML_PRESENT: - self.assertEqual(registry.lookup('lxml', 'xml'), - LXMLTreeBuilderForXML) - self.assertEqual(registry.lookup('lxml', 'html'), - LXMLTreeBuilder) + assert registry.lookup('lxml', 'xml') == LXMLTreeBuilderForXML + assert registry.lookup('lxml', 'html') == LXMLTreeBuilder if HTML5LIB_PRESENT: - self.assertEqual(registry.lookup('html5lib'), - HTML5TreeBuilder) + assert registry.lookup('html5lib') == HTML5TreeBuilder - self.assertEqual(registry.lookup('html.parser'), - HTMLParserTreeBuilder) + assert registry.lookup('html.parser') == HTMLParserTreeBuilder def test_beautifulsoup_constructor_does_lookup(self): @@ -77,16 +67,17 @@ class BuiltInRegistryTest(unittest.TestCase): BeautifulSoup("", features="html") # Or a list of strings. BeautifulSoup("", features=["html", "fast"]) - + pass + # You'll get an exception if BS can't find an appropriate # builder. - self.assertRaises(ValueError, BeautifulSoup, - "", features="no-such-feature") + with pytest.raises(ValueError): + BeautifulSoup("", features="no-such-feature") -class RegistryTest(unittest.TestCase): +class TestRegistry(object): """Test the TreeBuilderRegistry class in general.""" - def setUp(self): + def setup_method(self): self.registry = TreeBuilderRegistry() def builder_for_features(self, *feature_list): @@ -101,28 +92,28 @@ class RegistryTest(unittest.TestCase): # Since the builder advertises no features, you can't find it # by looking up features. - self.assertEqual(self.registry.lookup('foo'), None) + assert self.registry.lookup('foo') is None # But you can find it by doing a lookup with no features, if # this happens to be the only registered builder. - self.assertEqual(self.registry.lookup(), builder) + assert self.registry.lookup() == builder def test_register_with_features_makes_lookup_succeed(self): builder = self.builder_for_features('foo', 'bar') - self.assertEqual(self.registry.lookup('foo'), builder) - self.assertEqual(self.registry.lookup('bar'), builder) + assert self.registry.lookup('foo') is builder + assert self.registry.lookup('bar') is builder def test_lookup_fails_when_no_builder_implements_feature(self): builder = self.builder_for_features('foo', 'bar') - self.assertEqual(self.registry.lookup('baz'), None) + assert self.registry.lookup('baz') is None def test_lookup_gets_most_recent_registration_when_no_feature_specified(self): builder1 = self.builder_for_features('foo') builder2 = self.builder_for_features('bar') - self.assertEqual(self.registry.lookup(), builder2) + assert self.registry.lookup() == builder2 def test_lookup_fails_when_no_tree_builders_registered(self): - self.assertEqual(self.registry.lookup(), None) + assert self.registry.lookup() is None def test_lookup_gets_most_recent_builder_supporting_all_features(self): has_one = self.builder_for_features('foo') @@ -134,14 +125,12 @@ class RegistryTest(unittest.TestCase): # There are two builders featuring 'foo' and 'bar', but # the one that also features 'quux' was registered later. - self.assertEqual(self.registry.lookup('foo', 'bar'), - has_both_late) + assert self.registry.lookup('foo', 'bar') == has_both_late # There is only one builder featuring 'foo', 'bar', and 'baz'. - self.assertEqual(self.registry.lookup('foo', 'bar', 'baz'), - has_both_early) + assert self.registry.lookup('foo', 'bar', 'baz') == has_both_early def test_lookup_fails_when_cannot_reconcile_requested_features(self): builder1 = self.builder_for_features('foo', 'bar') builder2 = self.builder_for_features('foo', 'baz') - self.assertEqual(self.registry.lookup('bar', 'baz'), None) + assert self.registry.lookup('bar', 'baz') is None diff --git a/libs/common/bs4/tests/test_dammit.py b/libs/common/bs4/tests/test_dammit.py new file mode 100644 index 00000000..9971234e --- /dev/null +++ b/libs/common/bs4/tests/test_dammit.py @@ -0,0 +1,371 @@ +# encoding: utf-8 +import pytest +import logging +import bs4 +from bs4 import BeautifulSoup +from bs4.dammit import ( + EntitySubstitution, + EncodingDetector, + UnicodeDammit, +) + +class TestUnicodeDammit(object): + """Standalone tests of UnicodeDammit.""" + + def test_unicode_input(self): + markup = "I'm already Unicode! \N{SNOWMAN}" + dammit = UnicodeDammit(markup) + assert dammit.unicode_markup == markup + + def test_smart_quotes_to_unicode(self): + markup = b"\x91\x92\x93\x94" + dammit = UnicodeDammit(markup) + assert dammit.unicode_markup == "\u2018\u2019\u201c\u201d" + + def test_smart_quotes_to_xml_entities(self): + markup = b"\x91\x92\x93\x94" + dammit = UnicodeDammit(markup, smart_quotes_to="xml") + assert dammit.unicode_markup == "‘’“”" + + def test_smart_quotes_to_html_entities(self): + markup = b"\x91\x92\x93\x94" + dammit = UnicodeDammit(markup, smart_quotes_to="html") + assert dammit.unicode_markup == "‘’“”" + + def test_smart_quotes_to_ascii(self): + markup = b"\x91\x92\x93\x94" + dammit = UnicodeDammit(markup, smart_quotes_to="ascii") + assert dammit.unicode_markup == """''""""" + + def test_detect_utf8(self): + utf8 = b"Sacr\xc3\xa9 bleu! \xe2\x98\x83" + dammit = UnicodeDammit(utf8) + assert dammit.original_encoding.lower() == 'utf-8' + assert dammit.unicode_markup == 'Sacr\xe9 bleu! \N{SNOWMAN}' + + def test_convert_hebrew(self): + hebrew = b"\xed\xe5\xec\xf9" + dammit = UnicodeDammit(hebrew, ["iso-8859-8"]) + assert dammit.original_encoding.lower() == 'iso-8859-8' + assert dammit.unicode_markup == '\u05dd\u05d5\u05dc\u05e9' + + def test_dont_see_smart_quotes_where_there_are_none(self): + utf_8 = b"\343\202\261\343\203\274\343\202\277\343\202\244 Watch" + dammit = UnicodeDammit(utf_8) + assert dammit.original_encoding.lower() == 'utf-8' + assert dammit.unicode_markup.encode("utf-8") == utf_8 + + def test_ignore_inappropriate_codecs(self): + utf8_data = "Räksmörgås".encode("utf-8") + dammit = UnicodeDammit(utf8_data, ["iso-8859-8"]) + assert dammit.original_encoding.lower() == 'utf-8' + + def test_ignore_invalid_codecs(self): + utf8_data = "Räksmörgås".encode("utf-8") + for bad_encoding in ['.utf8', '...', 'utF---16.!']: + dammit = UnicodeDammit(utf8_data, [bad_encoding]) + assert dammit.original_encoding.lower() == 'utf-8' + + def test_exclude_encodings(self): + # This is UTF-8. + utf8_data = "Räksmörgås".encode("utf-8") + + # But if we exclude UTF-8 from consideration, the guess is + # Windows-1252. + dammit = UnicodeDammit(utf8_data, exclude_encodings=["utf-8"]) + assert dammit.original_encoding.lower() == 'windows-1252' + + # And if we exclude that, there is no valid guess at all. + dammit = UnicodeDammit( + utf8_data, exclude_encodings=["utf-8", "windows-1252"]) + assert dammit.original_encoding == None + +class TestEncodingDetector(object): + + def test_encoding_detector_replaces_junk_in_encoding_name_with_replacement_character(self): + detected = EncodingDetector( + b'') + encodings = list(detected.encodings) + assert 'utf-\N{REPLACEMENT CHARACTER}' in encodings + + def test_detect_html5_style_meta_tag(self): + + for data in ( + b'', + b"", + b"", + b""): + dammit = UnicodeDammit(data, is_html=True) + assert "euc-jp" == dammit.original_encoding + + def test_last_ditch_entity_replacement(self): + # This is a UTF-8 document that contains bytestrings + # completely incompatible with UTF-8 (ie. encoded with some other + # encoding). + # + # Since there is no consistent encoding for the document, + # Unicode, Dammit will eventually encode the document as UTF-8 + # and encode the incompatible characters as REPLACEMENT + # CHARACTER. + # + # If chardet is installed, it will detect that the document + # can be converted into ISO-8859-1 without errors. This happens + # to be the wrong encoding, but it is a consistent encoding, so the + # code we're testing here won't run. + # + # So we temporarily disable chardet if it's present. + doc = b"""\357\273\277 +\330\250\330\252\330\261 +\310\322\321\220\312\321\355\344""" + chardet = bs4.dammit.chardet_dammit + logging.disable(logging.WARNING) + try: + def noop(str): + return None + bs4.dammit.chardet_dammit = noop + dammit = UnicodeDammit(doc) + assert True == dammit.contains_replacement_characters + assert "\ufffd" in dammit.unicode_markup + + soup = BeautifulSoup(doc, "html.parser") + assert soup.contains_replacement_characters + finally: + logging.disable(logging.NOTSET) + bs4.dammit.chardet_dammit = chardet + + def test_byte_order_mark_removed(self): + # A document written in UTF-16LE will have its byte order marker stripped. + data = b'\xff\xfe<\x00a\x00>\x00\xe1\x00\xe9\x00<\x00/\x00a\x00>\x00' + dammit = UnicodeDammit(data) + assert "áé" == dammit.unicode_markup + assert "utf-16le" == dammit.original_encoding + + def test_known_definite_versus_user_encodings(self): + # The known_definite_encodings are used before sniffing the + # byte-order mark; the user_encodings are used afterwards. + + # Here's a document in UTF-16LE. + data = b'\xff\xfe<\x00a\x00>\x00\xe1\x00\xe9\x00<\x00/\x00a\x00>\x00' + dammit = UnicodeDammit(data) + + # We can process it as UTF-16 by passing it in as a known + # definite encoding. + before = UnicodeDammit(data, known_definite_encodings=["utf-16"]) + assert "utf-16" == before.original_encoding + + # If we pass UTF-18 as a user encoding, it's not even + # tried--the encoding sniffed from the byte-order mark takes + # precedence. + after = UnicodeDammit(data, user_encodings=["utf-8"]) + assert "utf-16le" == after.original_encoding + assert ["utf-16le"] == [x[0] for x in dammit.tried_encodings] + + # Here's a document in ISO-8859-8. + hebrew = b"\xed\xe5\xec\xf9" + dammit = UnicodeDammit(hebrew, known_definite_encodings=["utf-8"], + user_encodings=["iso-8859-8"]) + + # The known_definite_encodings don't work, BOM sniffing does + # nothing (it only works for a few UTF encodings), but one of + # the user_encodings does work. + assert "iso-8859-8" == dammit.original_encoding + assert ["utf-8", "iso-8859-8"] == [x[0] for x in dammit.tried_encodings] + + def test_deprecated_override_encodings(self): + # override_encodings is a deprecated alias for + # known_definite_encodings. + hebrew = b"\xed\xe5\xec\xf9" + dammit = UnicodeDammit( + hebrew, + known_definite_encodings=["shift-jis"], + override_encodings=["utf-8"], + user_encodings=["iso-8859-8"], + ) + assert "iso-8859-8" == dammit.original_encoding + + # known_definite_encodings and override_encodings were tried + # before user_encodings. + assert ["shift-jis", "utf-8", "iso-8859-8"] == ( + [x[0] for x in dammit.tried_encodings] + ) + + def test_detwingle(self): + # Here's a UTF8 document. + utf8 = ("\N{SNOWMAN}" * 3).encode("utf8") + + # Here's a Windows-1252 document. + windows_1252 = ( + "\N{LEFT DOUBLE QUOTATION MARK}Hi, I like Windows!" + "\N{RIGHT DOUBLE QUOTATION MARK}").encode("windows_1252") + + # Through some unholy alchemy, they've been stuck together. + doc = utf8 + windows_1252 + utf8 + + # The document can't be turned into UTF-8: + with pytest.raises(UnicodeDecodeError): + doc.decode("utf8") + + # Unicode, Dammit thinks the whole document is Windows-1252, + # and decodes it into "☃☃☃“Hi, I like Windows!”☃☃☃" + + # But if we run it through fix_embedded_windows_1252, it's fixed: + fixed = UnicodeDammit.detwingle(doc) + assert "☃☃☃“Hi, I like Windows!”☃☃☃" == fixed.decode("utf8") + + def test_detwingle_ignores_multibyte_characters(self): + # Each of these characters has a UTF-8 representation ending + # in \x93. \x93 is a smart quote if interpreted as + # Windows-1252. But our code knows to skip over multibyte + # UTF-8 characters, so they'll survive the process unscathed. + for tricky_unicode_char in ( + "\N{LATIN SMALL LIGATURE OE}", # 2-byte char '\xc5\x93' + "\N{LATIN SUBSCRIPT SMALL LETTER X}", # 3-byte char '\xe2\x82\x93' + "\xf0\x90\x90\x93", # This is a CJK character, not sure which one. + ): + input = tricky_unicode_char.encode("utf8") + assert input.endswith(b'\x93') + output = UnicodeDammit.detwingle(input) + assert output == input + + def test_find_declared_encoding(self): + # Test our ability to find a declared encoding inside an + # XML or HTML document. + # + # Even if the document comes in as Unicode, it may be + # interesting to know what encoding was claimed + # originally. + + html_unicode = '' + html_bytes = html_unicode.encode("ascii") + + xml_unicode= '' + xml_bytes = xml_unicode.encode("ascii") + + m = EncodingDetector.find_declared_encoding + assert m(html_unicode, is_html=False) is None + assert "utf-8" == m(html_unicode, is_html=True) + assert "utf-8" == m(html_bytes, is_html=True) + + assert "iso-8859-1" == m(xml_unicode) + assert "iso-8859-1" == m(xml_bytes) + + # Normally, only the first few kilobytes of a document are checked for + # an encoding. + spacer = b' ' * 5000 + assert m(spacer + html_bytes) is None + assert m(spacer + xml_bytes) is None + + # But you can tell find_declared_encoding to search an entire + # HTML document. + assert ( + m(spacer + html_bytes, is_html=True, search_entire_document=True) + == "utf-8" + ) + + # The XML encoding declaration has to be the very first thing + # in the document. We'll allow whitespace before the document + # starts, but nothing else. + assert m(xml_bytes, search_entire_document=True) == "iso-8859-1" + assert m(b' ' + xml_bytes, search_entire_document=True) == "iso-8859-1" + assert m(b'a' + xml_bytes, search_entire_document=True) is None + + +class TestEntitySubstitution(object): + """Standalone tests of the EntitySubstitution class.""" + def setup_method(self): + self.sub = EntitySubstitution + + def test_simple_html_substitution(self): + # Unicode characters corresponding to named HTML entites + # are substituted, and no others. + s = "foo\u2200\N{SNOWMAN}\u00f5bar" + assert self.sub.substitute_html(s) == "foo∀\N{SNOWMAN}õbar" + + def test_smart_quote_substitution(self): + # MS smart quotes are a common source of frustration, so we + # give them a special test. + quotes = b"\x91\x92foo\x93\x94" + dammit = UnicodeDammit(quotes) + assert self.sub.substitute_html(dammit.markup) == "‘’foo“”" + + def test_html5_entity(self): + # Some HTML5 entities correspond to single- or multi-character + # Unicode sequences. + + for entity, u in ( + # A few spot checks of our ability to recognize + # special character sequences and convert them + # to named entities. + ('⊧', '\u22a7'), + ('𝔑', '\U0001d511'), + ('≧̸', '\u2267\u0338'), + ('¬', '\xac'), + ('⫬', '\u2aec'), + + # We _could_ convert | to &verbarr;, but we don't, because + # | is an ASCII character. + ('|' '|'), + + # Similarly for the fj ligature, which we could convert to + # fj, but we don't. + ("fj", "fj"), + + # We do convert _these_ ASCII characters to HTML entities, + # because that's required to generate valid HTML. + ('>', '>'), + ('<', '<'), + ('&', '&'), + ): + template = '3 %s 4' + raw = template % u + with_entities = template % entity + assert self.sub.substitute_html(raw) == with_entities + + def test_html5_entity_with_variation_selector(self): + # Some HTML5 entities correspond either to a single-character + # Unicode sequence _or_ to the same character plus U+FE00, + # VARIATION SELECTOR 1. We can handle this. + data = "fjords \u2294 penguins" + markup = "fjords ⊔ penguins" + assert self.sub.substitute_html(data) == markup + + data = "fjords \u2294\ufe00 penguins" + markup = "fjords ⊔︀ penguins" + assert self.sub.substitute_html(data) == markup + + def test_xml_converstion_includes_no_quotes_if_make_quoted_attribute_is_false(self): + s = 'Welcome to "my bar"' + assert self.sub.substitute_xml(s, False) == s + + def test_xml_attribute_quoting_normally_uses_double_quotes(self): + assert self.sub.substitute_xml("Welcome", True) == '"Welcome"' + assert self.sub.substitute_xml("Bob's Bar", True) == '"Bob\'s Bar"' + + def test_xml_attribute_quoting_uses_single_quotes_when_value_contains_double_quotes(self): + s = 'Welcome to "my bar"' + assert self.sub.substitute_xml(s, True) == "'Welcome to \"my bar\"'" + + def test_xml_attribute_quoting_escapes_single_quotes_when_value_contains_both_single_and_double_quotes(self): + s = 'Welcome to "Bob\'s Bar"' + assert self.sub.substitute_xml(s, True) == '"Welcome to "Bob\'s Bar""' + + def test_xml_quotes_arent_escaped_when_value_is_not_being_quoted(self): + quoted = 'Welcome to "Bob\'s Bar"' + assert self.sub.substitute_xml(quoted) == quoted + + def test_xml_quoting_handles_angle_brackets(self): + assert self.sub.substitute_xml("foo") == "foo<bar>" + + def test_xml_quoting_handles_ampersands(self): + assert self.sub.substitute_xml("AT&T") == "AT&T" + + def test_xml_quoting_including_ampersands_when_they_are_part_of_an_entity(self): + assert self.sub.substitute_xml("ÁT&T") == "&Aacute;T&T" + + def test_xml_quoting_ignoring_ampersands_when_they_are_part_of_an_entity(self): + assert self.sub.substitute_xml_containing_entities("ÁT&T") == "ÁT&T" + + def test_quotes_not_html_substituted(self): + """There's no need to do this except inside attribute values.""" + text = 'Bob\'s "bar"' + assert self.sub.substitute_html(text) == text diff --git a/libs/common/bs4/tests/test_docs.py b/libs/common/bs4/tests/test_docs.py index 5b9f6770..0194d697 100644 --- a/libs/common/bs4/tests/test_docs.py +++ b/libs/common/bs4/tests/test_docs.py @@ -1,5 +1,7 @@ "Test harness for doctests." +# TODO: Pretty sure this isn't used and should be deleted. + # pylint: disable-msg=E0611,W0142 __metaclass__ = type diff --git a/libs/common/bs4/tests/test_element.py b/libs/common/bs4/tests/test_element.py new file mode 100644 index 00000000..6d08ab5d --- /dev/null +++ b/libs/common/bs4/tests/test_element.py @@ -0,0 +1,74 @@ +"""Tests of classes in element.py. + +The really big classes -- Tag, PageElement, and NavigableString -- +are tested in separate files. +""" + +from bs4.element import ( + CharsetMetaAttributeValue, + ContentMetaAttributeValue, + NamespacedAttribute, +) +from . import SoupTest + + +class TestNamedspacedAttribute(object): + + def test_name_may_be_none_or_missing(self): + a = NamespacedAttribute("xmlns", None) + assert a == "xmlns" + + a = NamespacedAttribute("xmlns", "") + assert a == "xmlns" + + a = NamespacedAttribute("xmlns") + assert a == "xmlns" + + def test_namespace_may_be_none_or_missing(self): + a = NamespacedAttribute(None, "tag") + assert a == "tag" + + a = NamespacedAttribute("", "tag") + assert a == "tag" + + def test_attribute_is_equivalent_to_colon_separated_string(self): + a = NamespacedAttribute("a", "b") + assert "a:b" == a + + def test_attributes_are_equivalent_if_prefix_and_name_identical(self): + a = NamespacedAttribute("a", "b", "c") + b = NamespacedAttribute("a", "b", "c") + assert a == b + + # The actual namespace is not considered. + c = NamespacedAttribute("a", "b", None) + assert a == c + + # But name and prefix are important. + d = NamespacedAttribute("a", "z", "c") + assert a != d + + e = NamespacedAttribute("z", "b", "c") + assert a != e + + +class TestAttributeValueWithCharsetSubstitution(object): + """Certain attributes are designed to have the charset of the + final document substituted into their value. + """ + + def test_content_meta_attribute_value(self): + # The value of a CharsetMetaAttributeValue is whatever + # encoding the string is in. + value = CharsetMetaAttributeValue("euc-jp") + assert "euc-jp" == value + assert "euc-jp" == value.original_value + assert "utf8" == value.encode("utf8") + assert "ascii" == value.encode("ascii") + + def test_content_meta_attribute_value(self): + value = ContentMetaAttributeValue("text/html; charset=euc-jp") + assert "text/html; charset=euc-jp" == value + assert "text/html; charset=euc-jp" == value.original_value + assert "text/html; charset=utf8" == value.encode("utf8") + assert "text/html; charset=ascii" == value.encode("ascii") diff --git a/libs/common/bs4/tests/test_formatter.py b/libs/common/bs4/tests/test_formatter.py new file mode 100644 index 00000000..84d4e3b2 --- /dev/null +++ b/libs/common/bs4/tests/test_formatter.py @@ -0,0 +1,113 @@ +import pytest + +from bs4.element import Tag +from bs4.formatter import ( + Formatter, + HTMLFormatter, + XMLFormatter, +) +from . import SoupTest + +class TestFormatter(SoupTest): + + def test_default_attributes(self): + # Test the default behavior of Formatter.attributes(). + formatter = Formatter() + tag = Tag(name="tag") + tag['b'] = 1 + tag['a'] = 2 + + # Attributes come out sorted by name. In Python 3, attributes + # normally come out of a dictionary in the order they were + # added. + assert [('a', 2), ('b', 1)] == formatter.attributes(tag) + + # This works even if Tag.attrs is None, though this shouldn't + # normally happen. + tag.attrs = None + assert [] == formatter.attributes(tag) + + assert ' ' == formatter.indent + + def test_sort_attributes(self): + # Test the ability to override Formatter.attributes() to, + # e.g., disable the normal sorting of attributes. + class UnsortedFormatter(Formatter): + def attributes(self, tag): + self.called_with = tag + for k, v in sorted(tag.attrs.items()): + if k == 'ignore': + continue + yield k,v + + soup = self.soup('

    ') + formatter = UnsortedFormatter() + decoded = soup.decode(formatter=formatter) + + # attributes() was called on the

    tag. It filtered out one + # attribute and sorted the other two. + assert formatter.called_with == soup.p + assert '

    ' == decoded + + def test_empty_attributes_are_booleans(self): + # Test the behavior of empty_attributes_are_booleans as well + # as which Formatters have it enabled. + + for name in ('html', 'minimal', None): + formatter = HTMLFormatter.REGISTRY[name] + assert False == formatter.empty_attributes_are_booleans + + formatter = XMLFormatter.REGISTRY[None] + assert False == formatter.empty_attributes_are_booleans + + formatter = HTMLFormatter.REGISTRY['html5'] + assert True == formatter.empty_attributes_are_booleans + + # Verify that the constructor sets the value. + formatter = Formatter(empty_attributes_are_booleans=True) + assert True == formatter.empty_attributes_are_booleans + + # Now demonstrate what it does to markup. + for markup in ( + "", + '' + ): + soup = self.soup(markup) + for formatter in ('html', 'minimal', 'xml', None): + assert b'' == soup.option.encode(formatter='html') + assert b'' == soup.option.encode(formatter='html5') + + @pytest.mark.parametrize( + "indent,expect", + [ + (None, '\n\ntext\n\n'), + (-1, '\n\ntext\n\n'), + (0, '\n\ntext\n\n'), + ("", '\n\ntext\n\n'), + + (1, '\n \n text\n \n'), + (2, '\n \n text\n \n'), + + ("\t", '\n\t\n\t\ttext\n\t\n'), + ('abc', '\nabc\nabcabctext\nabc\n'), + + # Some invalid inputs -- the default behavior is used. + (object(), '\n \n text\n \n'), + (b'bytes', '\n \n text\n \n'), + ] + ) + def test_indent(self, indent, expect): + # Pretty-print a tree with a Formatter set to + # indent in a certain way and verify the results. + soup = self.soup("text") + formatter = Formatter(indent=indent) + assert soup.prettify(formatter=formatter) == expect + + # Pretty-printing only happens with prettify(), not + # encode(). + assert soup.encode(formatter=formatter) != expect + + def test_default_indent_value(self): + formatter = Formatter() + assert formatter.indent == ' ' + diff --git a/libs/common/bs4/tests/test_html5lib.py b/libs/common/bs4/tests/test_html5lib.py index 81fb7d3b..b32ab304 100644 --- a/libs/common/bs4/tests/test_html5lib.py +++ b/libs/common/bs4/tests/test_html5lib.py @@ -8,7 +8,7 @@ try: except ImportError as e: HTML5LIB_PRESENT = False from bs4.element import SoupStrainer -from bs4.testing import ( +from . import ( HTML5TreeBuilderSmokeTest, SoupTest, skipIf, @@ -17,12 +17,12 @@ from bs4.testing import ( @skipIf( not HTML5LIB_PRESENT, "html5lib seems not to be present, not testing its tree builder.") -class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): +class TestHTML5LibBuilder(SoupTest, HTML5TreeBuilderSmokeTest): """See ``HTML5TreeBuilderSmokeTest``.""" @property def default_builder(self): - return HTML5TreeBuilder() + return HTML5TreeBuilder def test_soupstrainer(self): # The html5lib tree builder does not support SoupStrainers. @@ -30,12 +30,9 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): markup = "

    A bold statement.

    " with warnings.catch_warnings(record=True) as w: soup = self.soup(markup, parse_only=strainer) - self.assertEqual( - soup.decode(), self.document_for(markup)) + assert soup.decode() == self.document_for(markup) - self.assertTrue( - "the html5lib tree builder doesn't support parse_only" in - str(w[0].message)) + assert "the html5lib tree builder doesn't support parse_only" in str(w[0].message) def test_correctly_nested_tables(self): """html5lib inserts tags where other parsers don't.""" @@ -46,13 +43,13 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): 'foo' '') - self.assertSoupEquals( + self.assert_soup( markup, '
    Here\'s another table:' '
    foo
    ' '
    ') - self.assertSoupEquals( + self.assert_soup( "" "" "
    Foo
    Bar
    Baz
    ") @@ -69,20 +66,20 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): ''' soup = self.soup(markup) # Verify that we can reach the

    tag; this means the tree is connected. - self.assertEqual(b"

    foo

    ", soup.p.encode()) + assert b"

    foo

    " == soup.p.encode() def test_reparented_markup(self): markup = '

    foo

    \n

    bar

    ' soup = self.soup(markup) - self.assertEqual("

    foo

    \n

    bar

    ", soup.body.decode()) - self.assertEqual(2, len(soup.find_all('p'))) + assert "

    foo

    \n

    bar

    " == soup.body.decode() + assert 2 == len(soup.find_all('p')) def test_reparented_markup_ends_with_whitespace(self): markup = '

    foo

    \n

    bar

    \n' soup = self.soup(markup) - self.assertEqual("

    foo

    \n

    bar

    \n", soup.body.decode()) - self.assertEqual(2, len(soup.find_all('p'))) + assert "

    foo

    \n

    bar

    \n" == soup.body.decode() + assert 2 == len(soup.find_all('p')) def test_reparented_markup_containing_identical_whitespace_nodes(self): """Verify that we keep the two whitespace nodes in this @@ -99,7 +96,7 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): markup = '' soup = self.soup(markup) noscript = soup.noscript - self.assertEqual("target", noscript.next_element) + assert "target" == noscript.next_element target = soup.find(string='target') # The 'aftermath' string was duplicated; we want the second one. @@ -108,8 +105,8 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest): # The

    =l z*8PZ?4@_E35785JIU8rqu%56^fj$v_yM3Gb(D|jlP3QQva5cm$gkmIG3mo5w-^5$N z*KEhT@C1@UW|D=lerHIHdC&}@QFJ)XqpRo{`T|?Q(!guX64T^dxmdpB9QL=hB@nCF zGZ+=3r_g%ThxS;9ZxgqJo;CRJvBFP=(x8y$J_mLma{q!^XCFmQ(wrIbU&oT^v;F_*-Q`}T{qMPAg zj}w;ykDSzz`w<8dNEAYlqFlo%#b#AuN!(!@mY zIvFBMy`W2i|7M zTv;HCWSR8I*|JJj%XzX^c7RQXfUd;Zsdl=ZVQ0afq5#yq-TB7paE>@3y0;F4-9)6m zNqcpSjsqXgwbH|MiXN>~b()^2({-8l>DjtUSL=DYR?pXUy57&au16OFT?lj`(1k!3 z0{?diL=Vc58D(>P1*Ih!B+$a&>=&;kcSphqe|~gU;a?;KNDJ5Tvl4{^OombIjWdb5gM9bURvy} zm^rPeu(+@!yC^ZIsL*@)lRupe4K2*~W@eT^64cC0uj6=Quk;ry;U_Y$7n(}UE}vdG z72WN1po>JP8&@71H?$(Bys*rd0mGW*R%Q6767R;uCU|4vc}=DGtF7cgD&@GJdX9^a rhnWXm7aoY>24B(s!`uU)z)Q7%J9I4)X_xbn1i`*bz$JgcFpuY7_#z;> diff --git a/libs/common/bin/unidecode.exe b/libs/common/bin/unidecode.exe index 0880f1b8075589f2dd9616e0bf5b16aad99d0bd1..7fd3c5dc46cc4032af72aaa40899ddb07a9540ac 100644 GIT binary patch literal 108375 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 diff --git a/libs/common/colorama/__init__.py b/libs/common/colorama/__init__.py index 2a3bf471..383101cd 100644 --- a/libs/common/colorama/__init__.py +++ b/libs/common/colorama/__init__.py @@ -1,6 +1,7 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from .initialise import init, deinit, reinit, colorama_text +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console from .ansi import Fore, Back, Style, Cursor from .ansitowin32 import AnsiToWin32 -__version__ = '0.4.1' +__version__ = '0.4.6' + diff --git a/libs/common/colorama/ansi.py b/libs/common/colorama/ansi.py index 78776588..11ec695f 100644 --- a/libs/common/colorama/ansi.py +++ b/libs/common/colorama/ansi.py @@ -6,7 +6,7 @@ See: http://en.wikipedia.org/wiki/ANSI_escape_code CSI = '\033[' OSC = '\033]' -BEL = '\007' +BEL = '\a' def code_to_chars(code): diff --git a/libs/common/colorama/ansitowin32.py b/libs/common/colorama/ansitowin32.py index 359c92be..abf209e6 100644 --- a/libs/common/colorama/ansitowin32.py +++ b/libs/common/colorama/ansitowin32.py @@ -3,8 +3,8 @@ import re import sys import os -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style -from .winterm import WinTerm, WinColor, WinStyle +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle from .win32 import windll, winapi_test @@ -37,6 +37,12 @@ class StreamWrapper(object): def __exit__(self, *args, **kwargs): return self.__wrapped.__exit__(*args, **kwargs) + def __setstate__(self, state): + self.__dict__ = state + + def __getstate__(self): + return self.__dict__ + def write(self, text): self.__convertor.write(text) @@ -57,7 +63,9 @@ class StreamWrapper(object): stream = self.__wrapped try: return stream.closed - except AttributeError: + # AttributeError in the case that the stream doesn't support being closed + # ValueError for the case that the stream has already been detached when atexit runs + except (AttributeError, ValueError): return True @@ -68,7 +76,7 @@ class AnsiToWin32(object): win32 function calls. ''' ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) @@ -86,15 +94,22 @@ class AnsiToWin32(object): # (e.g. Cygwin Terminal). In this case it's up to the terminal # to support the ANSI codes. conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi # should we strip ANSI sequences from our output? if strip is None: - strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) + strip = need_conversion or not have_tty self.strip = strip # should we should convert ANSI sequences into win32 calls? if convert is None: - convert = conversion_supported and not self.stream.closed and self.stream.isatty() + convert = need_conversion and have_tty self.convert = convert # dict of ansi codes to win32 functions and parameters @@ -247,11 +262,16 @@ class AnsiToWin32(object): start, end = match.span() text = text[:start] + text[end:] paramstring, command = match.groups() - if command in '\x07': # \x07 = BEL - params = paramstring.split(";") - # 0 - change title and icon (we will only change title) - # 1 - change icon (we don't support this) - # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) return text + + + def flush(self): + self.wrapped.flush() diff --git a/libs/common/colorama/initialise.py b/libs/common/colorama/initialise.py index 430d0668..d5fd4b71 100644 --- a/libs/common/colorama/initialise.py +++ b/libs/common/colorama/initialise.py @@ -6,13 +6,27 @@ import sys from .ansitowin32 import AnsiToWin32 -orig_stdout = None -orig_stderr = None +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None -wrapped_stdout = None -wrapped_stderr = None + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None -atexit_done = False + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False + + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass def reset_all(): @@ -55,6 +69,29 @@ def deinit(): sys.stderr = orig_stderr +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + @contextlib.contextmanager def colorama_text(*args, **kwargs): init(*args, **kwargs) @@ -78,3 +115,7 @@ def wrap_stream(stream, convert, strip, autoreset, wrap): if wrapper.should_wrap(): stream = wrapper.stream return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/libs/common/colorama/tests/__init__.py b/libs/common/colorama/tests/__init__.py new file mode 100644 index 00000000..8c5661e9 --- /dev/null +++ b/libs/common/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/libs/common/colorama/tests/ansi_test.py b/libs/common/colorama/tests/ansi_test.py new file mode 100644 index 00000000..0a20c80f --- /dev/null +++ b/libs/common/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/libs/common/colorama/tests/ansitowin32_test.py b/libs/common/colorama/tests/ansitowin32_test.py new file mode 100644 index 00000000..91ca551f --- /dev/null +++ b/libs/common/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/libs/common/colorama/tests/initialise_test.py b/libs/common/colorama/tests/initialise_test.py new file mode 100644 index 00000000..89f9b075 --- /dev/null +++ b/libs/common/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/libs/common/colorama/tests/isatty_test.py b/libs/common/colorama/tests/isatty_test.py new file mode 100644 index 00000000..0f84e4be --- /dev/null +++ b/libs/common/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/libs/common/colorama/tests/utils.py b/libs/common/colorama/tests/utils.py new file mode 100644 index 00000000..472fafb4 --- /dev/null +++ b/libs/common/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/libs/common/colorama/tests/winterm_test.py b/libs/common/colorama/tests/winterm_test.py new file mode 100644 index 00000000..d0955f9e --- /dev/null +++ b/libs/common/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/libs/common/colorama/win32.py b/libs/common/colorama/win32.py index c2d83603..841b0e27 100644 --- a/libs/common/colorama/win32.py +++ b/libs/common/colorama/win32.py @@ -4,6 +4,8 @@ STDOUT = -11 STDERR = -12 +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + try: import ctypes from ctypes import LibraryLoader @@ -89,6 +91,20 @@ else: ] _SetConsoleTitleW.restype = wintypes.BOOL + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + def _winapi_test(handle): csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( @@ -150,3 +166,15 @@ else: def SetConsoleTitle(title): return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/libs/common/colorama/winterm.py b/libs/common/colorama/winterm.py index 0fdb4ec4..aad867e8 100644 --- a/libs/common/colorama/winterm.py +++ b/libs/common/colorama/winterm.py @@ -1,7 +1,13 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from . import win32 +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") +from . import win32 + # from wincon.h class WinColor(object): BLACK = 0 @@ -167,3 +173,23 @@ class WinTerm(object): def set_title(self, title): win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/libs/common/confuse/__init__.py b/libs/common/confuse/__init__.py new file mode 100644 index 00000000..be5090c4 --- /dev/null +++ b/libs/common/confuse/__init__.py @@ -0,0 +1,13 @@ +"""Painless YAML configuration. +""" + +from __future__ import division, absolute_import, print_function + +__version__ = '1.7.0' + +from .exceptions import * # NOQA +from .util import * # NOQA +from .yaml_util import * # NOQA +from .sources import * # NOQA +from .templates import * # NOQA +from .core import * # NOQA diff --git a/libs/common/confuse/core.py b/libs/common/confuse/core.py new file mode 100644 index 00000000..6c6c4b09 --- /dev/null +++ b/libs/common/confuse/core.py @@ -0,0 +1,724 @@ +# -*- coding: utf-8 -*- +# This file is part of Confuse. +# Copyright 2016, Adrian Sampson. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Worry-free YAML configuration files. +""" +from __future__ import division, absolute_import, print_function + +import errno +import os +import yaml +from collections import OrderedDict + +from . import util +from . import templates +from . import yaml_util +from .sources import ConfigSource, EnvSource, YamlSource +from .exceptions import ConfigTypeError, NotFoundError, ConfigError + +CONFIG_FILENAME = 'config.yaml' +DEFAULT_FILENAME = 'config_default.yaml' +ROOT_NAME = 'root' + +REDACTED_TOMBSTONE = 'REDACTED' + + +# Views and sources. + + +class ConfigView(object): + """A configuration "view" is a query into a program's configuration + data. A view represents a hypothetical location in the configuration + tree; to extract the data from the location, a client typically + calls the ``view.get()`` method. The client can access children in + the tree (subviews) by subscripting the parent view (i.e., + ``view[key]``). + """ + + name = None + """The name of the view, depicting the path taken through the + configuration in Python-like syntax (e.g., ``foo['bar'][42]``). + """ + + def resolve(self): + """The core (internal) data retrieval method. Generates (value, + source) pairs for each source that contains a value for this + view. May raise `ConfigTypeError` if a type error occurs while + traversing a source. + """ + raise NotImplementedError + + def first(self): + """Return a (value, source) pair for the first object found for + this view. This amounts to the first element returned by + `resolve`. If no values are available, a `NotFoundError` is + raised. + """ + pairs = self.resolve() + try: + return util.iter_first(pairs) + except ValueError: + raise NotFoundError(u"{0} not found".format(self.name)) + + def exists(self): + """Determine whether the view has a setting in any source. + """ + try: + self.first() + except NotFoundError: + return False + return True + + def add(self, value): + """Set the *default* value for this configuration view. The + specified value is added as the lowest-priority configuration + data source. + """ + raise NotImplementedError + + def set(self, value): + """*Override* the value for this configuration view. The + specified value is added as the highest-priority configuration + data source. + """ + raise NotImplementedError + + def root(self): + """The RootView object from which this view is descended. + """ + raise NotImplementedError + + def __repr__(self): + return '<{}: {}>'.format(self.__class__.__name__, self.name) + + def __iter__(self): + """Iterate over the keys of a dictionary view or the *subviews* + of a list view. + """ + # Try iterating over the keys, if this is a dictionary view. + try: + for key in self.keys(): + yield key + + except ConfigTypeError: + # Otherwise, try iterating over a list view. + try: + for subview in self.sequence(): + yield subview + + except ConfigTypeError: + item, _ = self.first() + raise ConfigTypeError( + u'{0} must be a dictionary or a list, not {1}'.format( + self.name, type(item).__name__ + ) + ) + + def __getitem__(self, key): + """Get a subview of this view.""" + return Subview(self, key) + + def __setitem__(self, key, value): + """Create an overlay source to assign a given key under this + view. + """ + self.set({key: value}) + + def __contains__(self, key): + return self[key].exists() + + def set_args(self, namespace, dots=False): + """Overlay parsed command-line arguments, generated by a library + like argparse or optparse, onto this view's value. + + :param namespace: Dictionary or Namespace to overlay this config with. + Supports nested Dictionaries and Namespaces. + :type namespace: dict or Namespace + :param dots: If True, any properties on namespace that contain dots (.) + will be broken down into child dictionaries. + :Example: + + {'foo.bar': 'car'} + # Will be turned into + {'foo': {'bar': 'car'}} + :type dots: bool + """ + self.set(util.build_dict(namespace, sep='.' if dots else '')) + + # Magical conversions. These special methods make it possible to use + # View objects somewhat transparently in certain circumstances. For + # example, rather than using ``view.get(bool)``, it's possible to + # just say ``bool(view)`` or use ``view`` in a conditional. + + def __str__(self): + """Get the value for this view as a bytestring. + """ + if util.PY3: + return self.__unicode__() + else: + return bytes(self.get()) + + def __unicode__(self): + """Get the value for this view as a Unicode string. + """ + return util.STRING(self.get()) + + def __nonzero__(self): + """Gets the value for this view as a boolean. (Python 2 only.) + """ + return self.__bool__() + + def __bool__(self): + """Gets the value for this view as a boolean. (Python 3 only.) + """ + return bool(self.get()) + + # Dictionary emulation methods. + + def keys(self): + """Returns a list containing all the keys available as subviews + of the current views. This enumerates all the keys in *all* + dictionaries matching the current view, in contrast to + ``view.get(dict).keys()``, which gets all the keys for the + *first* dict matching the view. If the object for this view in + any source is not a dict, then a `ConfigTypeError` is raised. The + keys are ordered according to how they appear in each source. + """ + keys = [] + + for dic, _ in self.resolve(): + try: + cur_keys = dic.keys() + except AttributeError: + raise ConfigTypeError( + u'{0} must be a dict, not {1}'.format( + self.name, type(dic).__name__ + ) + ) + + for key in cur_keys: + if key not in keys: + keys.append(key) + + return keys + + def items(self): + """Iterates over (key, subview) pairs contained in dictionaries + from *all* sources at this view. If the object for this view in + any source is not a dict, then a `ConfigTypeError` is raised. + """ + for key in self.keys(): + yield key, self[key] + + def values(self): + """Iterates over all the subviews contained in dictionaries from + *all* sources at this view. If the object for this view in any + source is not a dict, then a `ConfigTypeError` is raised. + """ + for key in self.keys(): + yield self[key] + + # List/sequence emulation. + + def sequence(self): + """Iterates over the subviews contained in lists from the *first* + source at this view. If the object for this view in the first source + is not a list or tuple, then a `ConfigTypeError` is raised. + """ + try: + collection, _ = self.first() + except NotFoundError: + return + if not isinstance(collection, (list, tuple)): + raise ConfigTypeError( + u'{0} must be a list, not {1}'.format( + self.name, type(collection).__name__ + ) + ) + + # Yield all the indices in the sequence. + for index in range(len(collection)): + yield self[index] + + def all_contents(self): + """Iterates over all subviews from collections at this view from + *all* sources. If the object for this view in any source is not + iterable, then a `ConfigTypeError` is raised. This method is + intended to be used when the view indicates a list; this method + will concatenate the contents of the list from all sources. + """ + for collection, _ in self.resolve(): + try: + it = iter(collection) + except TypeError: + raise ConfigTypeError( + u'{0} must be an iterable, not {1}'.format( + self.name, type(collection).__name__ + ) + ) + for value in it: + yield value + + # Validation and conversion. + + def flatten(self, redact=False): + """Create a hierarchy of OrderedDicts containing the data from + this view, recursively reifying all views to get their + represented values. + + If `redact` is set, then sensitive values are replaced with + the string "REDACTED". + """ + od = OrderedDict() + for key, view in self.items(): + if redact and view.redact: + od[key] = REDACTED_TOMBSTONE + else: + try: + od[key] = view.flatten(redact=redact) + except ConfigTypeError: + od[key] = view.get() + return od + + def get(self, template=templates.REQUIRED): + """Retrieve the value for this view according to the template. + + The `template` against which the values are checked can be + anything convertible to a `Template` using `as_template`. This + means you can pass in a default integer or string value, for + example, or a type to just check that something matches the type + you expect. + + May raise a `ConfigValueError` (or its subclass, + `ConfigTypeError`) or a `NotFoundError` when the configuration + doesn't satisfy the template. + """ + return templates.as_template(template).value(self, template) + + # Shortcuts for common templates. + + def as_filename(self): + """Get the value as a path. Equivalent to `get(Filename())`. + """ + return self.get(templates.Filename()) + + def as_path(self): + """Get the value as a `pathlib.Path` object. Equivalent to `get(Path())`. + """ + return self.get(templates.Path()) + + def as_choice(self, choices): + """Get the value from a list of choices. Equivalent to + `get(Choice(choices))`. + """ + return self.get(templates.Choice(choices)) + + def as_number(self): + """Get the value as any number type: int or float. Equivalent to + `get(Number())`. + """ + return self.get(templates.Number()) + + def as_str_seq(self, split=True): + """Get the value as a sequence of strings. Equivalent to + `get(StrSeq(split=split))`. + """ + return self.get(templates.StrSeq(split=split)) + + def as_pairs(self, default_value=None): + """Get the value as a sequence of pairs of two strings. Equivalent to + `get(Pairs(default_value=default_value))`. + """ + return self.get(templates.Pairs(default_value=default_value)) + + def as_str(self): + """Get the value as a (Unicode) string. Equivalent to + `get(unicode)` on Python 2 and `get(str)` on Python 3. + """ + return self.get(templates.String()) + + def as_str_expanded(self): + """Get the value as a (Unicode) string, with env vars + expanded by `os.path.expandvars()`. + """ + return self.get(templates.String(expand_vars=True)) + + # Redaction. + + @property + def redact(self): + """Whether the view contains sensitive information and should be + redacted from output. + """ + return () in self.get_redactions() + + @redact.setter + def redact(self, flag): + self.set_redaction((), flag) + + def set_redaction(self, path, flag): + """Add or remove a redaction for a key path, which should be an + iterable of keys. + """ + raise NotImplementedError() + + def get_redactions(self): + """Get the set of currently-redacted sub-key-paths at this view. + """ + raise NotImplementedError() + + +class RootView(ConfigView): + """The base of a view hierarchy. This view keeps track of the + sources that may be accessed by subviews. + """ + def __init__(self, sources): + """Create a configuration hierarchy for a list of sources. At + least one source must be provided. The first source in the list + has the highest priority. + """ + self.sources = list(sources) + self.name = ROOT_NAME + self.redactions = set() + + def add(self, obj): + self.sources.append(ConfigSource.of(obj)) + + def set(self, value): + self.sources.insert(0, ConfigSource.of(value)) + + def resolve(self): + return ((dict(s), s) for s in self.sources) + + def clear(self): + """Remove all sources (and redactions) from this + configuration. + """ + del self.sources[:] + self.redactions.clear() + + def root(self): + return self + + def set_redaction(self, path, flag): + if flag: + self.redactions.add(path) + elif path in self.redactions: + self.redactions.remove(path) + + def get_redactions(self): + return self.redactions + + +class Subview(ConfigView): + """A subview accessed via a subscript of a parent view.""" + def __init__(self, parent, key): + """Make a subview of a parent view for a given subscript key. + """ + self.parent = parent + self.key = key + + # Choose a human-readable name for this view. + if isinstance(self.parent, RootView): + self.name = '' + else: + self.name = self.parent.name + if not isinstance(self.key, int): + self.name += '.' + if isinstance(self.key, int): + self.name += u'#{0}'.format(self.key) + elif isinstance(self.key, bytes): + self.name += self.key.decode('utf-8') + elif isinstance(self.key, util.STRING): + self.name += self.key + else: + self.name += repr(self.key) + + def resolve(self): + for collection, source in self.parent.resolve(): + try: + value = collection[self.key] + except IndexError: + # List index out of bounds. + continue + except KeyError: + # Dict key does not exist. + continue + except TypeError: + # Not subscriptable. + raise ConfigTypeError( + u"{0} must be a collection, not {1}".format( + self.parent.name, type(collection).__name__ + ) + ) + yield value, source + + def set(self, value): + self.parent.set({self.key: value}) + + def add(self, value): + self.parent.add({self.key: value}) + + def root(self): + return self.parent.root() + + def set_redaction(self, path, flag): + self.parent.set_redaction((self.key,) + path, flag) + + def get_redactions(self): + return (kp[1:] for kp in self.parent.get_redactions() + if kp and kp[0] == self.key) + +# Main interface. + + +class Configuration(RootView): + def __init__(self, appname, modname=None, read=True, + loader=yaml_util.Loader): + """Create a configuration object by reading the + automatically-discovered config files for the application for a + given name. If `modname` is specified, it should be the import + name of a module whose package will be searched for a default + config file. (Otherwise, no defaults are used.) Pass `False` for + `read` to disable automatic reading of all discovered + configuration files. Use this when creating a configuration + object at module load time and then call the `read` method + later. Specify the Loader class as `loader`. + """ + super(Configuration, self).__init__([]) + self.appname = appname + self.modname = modname + self.loader = loader + + # Resolve default source location. We do this ahead of time to + # avoid unexpected problems if the working directory changes. + if self.modname: + self._package_path = util.find_package_path(self.modname) + else: + self._package_path = None + + self._env_var = '{0}DIR'.format(self.appname.upper()) + + if read: + self.read() + + def user_config_path(self): + """Points to the location of the user configuration. + + The file may not exist. + """ + return os.path.join(self.config_dir(), CONFIG_FILENAME) + + def _add_user_source(self): + """Add the configuration options from the YAML file in the + user's configuration directory (given by `config_dir`) if it + exists. + """ + filename = self.user_config_path() + self.add(YamlSource(filename, loader=self.loader, optional=True)) + + def _add_default_source(self): + """Add the package's default configuration settings. This looks + for a YAML file located inside the package for the module + `modname` if it was given. + """ + if self.modname: + if self._package_path: + filename = os.path.join(self._package_path, DEFAULT_FILENAME) + self.add(YamlSource(filename, loader=self.loader, + optional=True, default=True)) + + def read(self, user=True, defaults=True): + """Find and read the files for this configuration and set them + as the sources for this configuration. To disable either + discovered user configuration files or the in-package defaults, + set `user` or `defaults` to `False`. + """ + if user: + self._add_user_source() + if defaults: + self._add_default_source() + + def config_dir(self): + """Get the path to the user configuration directory. The + directory is guaranteed to exist as a postcondition (one may be + created if none exist). + + If the application's ``...DIR`` environment variable is set, it + is used as the configuration directory. Otherwise, + platform-specific standard configuration locations are searched + for a ``config.yaml`` file. If no configuration file is found, a + fallback path is used. + """ + # If environment variable is set, use it. + if self._env_var in os.environ: + appdir = os.environ[self._env_var] + appdir = os.path.abspath(os.path.expanduser(appdir)) + if os.path.isfile(appdir): + raise ConfigError(u'{0} must be a directory'.format( + self._env_var + )) + + else: + # Search platform-specific locations. If no config file is + # found, fall back to the first directory in the list. + configdirs = util.config_dirs() + for confdir in configdirs: + appdir = os.path.join(confdir, self.appname) + if os.path.isfile(os.path.join(appdir, CONFIG_FILENAME)): + break + else: + appdir = os.path.join(configdirs[0], self.appname) + + # Ensure that the directory exists. + try: + os.makedirs(appdir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + return appdir + + def set_file(self, filename, base_for_paths=False): + """Parses the file as YAML and inserts it into the configuration + sources with highest priority. + + :param filename: Filename of the YAML file to load. + :param base_for_paths: Indicates whether the directory containing the + YAML file will be used as the base directory for resolving relative + path values stored in the YAML file. Otherwise, by default, the + directory returned by `config_dir()` will be used as the base. + """ + self.set(YamlSource(filename, base_for_paths=base_for_paths, + loader=self.loader)) + + def set_env(self, prefix=None, sep='__'): + """Create a configuration overlay at the highest priority from + environment variables. + + After prefix matching and removal, environment variable names will be + converted to lowercase for use as keys within the configuration. If + there are nested keys, list-like dicts (ie, `{0: 'a', 1: 'b'}`) will + be converted into corresponding lists (ie, `['a', 'b']`). The values + of all environment variables will be parsed as YAML scalars using the + `self.loader` Loader class to ensure type conversion is consistent + with YAML file sources. Use the `EnvSource` class directly to load + environment variables using non-default behavior and to enable full + YAML parsing of values. + + :param prefix: The prefix to identify the environment variables to use. + Defaults to uppercased `self.appname` followed by an underscore. + :param sep: Separator within variable names to define nested keys. + """ + if prefix is None: + prefix = '{0}_'.format(self.appname.upper()) + self.set(EnvSource(prefix, sep=sep, loader=self.loader)) + + def dump(self, full=True, redact=False): + """Dump the Configuration object to a YAML file. + + The order of the keys is determined from the default + configuration file. All keys not in the default configuration + will be appended to the end of the file. + + :param full: Dump settings that don't differ from the defaults + as well + :param redact: Remove sensitive information (views with the `redact` + flag set) from the output + """ + if full: + out_dict = self.flatten(redact=redact) + else: + # Exclude defaults when flattening. + sources = [s for s in self.sources if not s.default] + temp_root = RootView(sources) + temp_root.redactions = self.redactions + out_dict = temp_root.flatten(redact=redact) + + yaml_out = yaml.dump(out_dict, Dumper=yaml_util.Dumper, + default_flow_style=None, indent=4, + width=1000) + + # Restore comments to the YAML text. + default_source = None + for source in self.sources: + if source.default: + default_source = source + break + if default_source and default_source.filename: + with open(default_source.filename, 'rb') as fp: + default_data = fp.read() + yaml_out = yaml_util.restore_yaml_comments( + yaml_out, default_data.decode('utf-8')) + + return yaml_out + + def reload(self): + """Reload all sources from the file system. + + This only affects sources that come from files (i.e., + `YamlSource` objects); other sources, such as dictionaries + inserted with `add` or `set`, will remain unchanged. + """ + for source in self.sources: + if isinstance(source, YamlSource): + source.load() + + +class LazyConfig(Configuration): + """A Configuration at reads files on demand when it is first + accessed. This is appropriate for using as a global config object at + the module level. + """ + def __init__(self, appname, modname=None): + super(LazyConfig, self).__init__(appname, modname, False) + self._materialized = False # Have we read the files yet? + self._lazy_prefix = [] # Pre-materialization calls to set(). + self._lazy_suffix = [] # Calls to add(). + + def read(self, user=True, defaults=True): + self._materialized = True + super(LazyConfig, self).read(user, defaults) + + def resolve(self): + if not self._materialized: + # Read files and unspool buffers. + self.read() + self.sources += self._lazy_suffix + self.sources[:0] = self._lazy_prefix + return super(LazyConfig, self).resolve() + + def add(self, value): + super(LazyConfig, self).add(value) + if not self._materialized: + # Buffer additions to end. + self._lazy_suffix += self.sources + del self.sources[:] + + def set(self, value): + super(LazyConfig, self).set(value) + if not self._materialized: + # Buffer additions to beginning. + self._lazy_prefix[:0] = self.sources + del self.sources[:] + + def clear(self): + """Remove all sources from this configuration.""" + super(LazyConfig, self).clear() + self._lazy_suffix = [] + self._lazy_prefix = [] + + +# "Validated" configuration views: experimental! diff --git a/libs/common/confuse/exceptions.py b/libs/common/confuse/exceptions.py new file mode 100644 index 00000000..782260ea --- /dev/null +++ b/libs/common/confuse/exceptions.py @@ -0,0 +1,56 @@ +from __future__ import division, absolute_import, print_function + +import yaml + +__all__ = [ + 'ConfigError', 'NotFoundError', 'ConfigValueError', 'ConfigTypeError', + 'ConfigTemplateError', 'ConfigReadError'] + +YAML_TAB_PROBLEM = "found character '\\t' that cannot start any token" + +# Exceptions. + + +class ConfigError(Exception): + """Base class for exceptions raised when querying a configuration. + """ + + +class NotFoundError(ConfigError): + """A requested value could not be found in the configuration trees. + """ + + +class ConfigValueError(ConfigError): + """The value in the configuration is illegal.""" + + +class ConfigTypeError(ConfigValueError): + """The value in the configuration did not match the expected type. + """ + + +class ConfigTemplateError(ConfigError): + """Base class for exceptions raised because of an invalid template. + """ + + +class ConfigReadError(ConfigError): + """A configuration source could not be read.""" + def __init__(self, name, reason=None): + self.name = name + self.reason = reason + + message = u'{0} could not be read'.format(name) + if (isinstance(reason, yaml.scanner.ScannerError) + and reason.problem == YAML_TAB_PROBLEM): + # Special-case error message for tab indentation in YAML markup. + message += u': found tab character at line {0}, column {1}'.format( + reason.problem_mark.line + 1, + reason.problem_mark.column + 1, + ) + elif reason: + # Generic error message uses exception's message. + message += u': {0}'.format(reason) + + super(ConfigReadError, self).__init__(message) diff --git a/libs/common/confuse/sources.py b/libs/common/confuse/sources.py new file mode 100644 index 00000000..2b0f53ba --- /dev/null +++ b/libs/common/confuse/sources.py @@ -0,0 +1,184 @@ +from __future__ import division, absolute_import, print_function + +from .util import BASESTRING, build_dict +from . import yaml_util +import os + + +class ConfigSource(dict): + """A dictionary augmented with metadata about the source of the + configuration. + """ + def __init__(self, value, filename=None, default=False, + base_for_paths=False): + """Create a configuration source from a dictionary. + + :param filename: The file with the data for this configuration source. + + :param default: Indicates whether this source provides the + application's default configuration settings. + + :param base_for_paths: Indicates whether the source file's directory + (i.e., the directory component of `self.filename`) should be used as + the base directory for resolving relative path values provided by this + source, instead of using the application's configuration directory. If + no `filename` is provided, `base_for_paths` will be treated as False. + See `templates.Filename` for details of the relative path resolution + behavior. + """ + super(ConfigSource, self).__init__(value) + if (filename is not None + and not isinstance(filename, BASESTRING)): + raise TypeError(u'filename must be a string or None') + self.filename = filename + self.default = default + self.base_for_paths = base_for_paths if filename is not None else False + + def __repr__(self): + return 'ConfigSource({0!r}, {1!r}, {2!r}, {3!r})'.format( + super(ConfigSource, self), + self.filename, + self.default, + self.base_for_paths, + ) + + @classmethod + def of(cls, value): + """Given either a dictionary or a `ConfigSource` object, return + a `ConfigSource` object. This lets a function accept either type + of object as an argument. + """ + if isinstance(value, ConfigSource): + return value + elif isinstance(value, dict): + return ConfigSource(value) + else: + raise TypeError(u'source value must be a dict') + + +class YamlSource(ConfigSource): + """A configuration data source that reads from a YAML file. + """ + + def __init__(self, filename=None, default=False, base_for_paths=False, + optional=False, loader=yaml_util.Loader): + """Create a YAML data source by reading data from a file. + + May raise a `ConfigReadError`. However, if `optional` is + enabled, this exception will not be raised in the case when the + file does not exist---instead, the source will be silently + empty. + """ + filename = os.path.abspath(filename) + super(YamlSource, self).__init__({}, filename, default, base_for_paths) + self.loader = loader + self.optional = optional + self.load() + + def load(self): + """Load YAML data from the source's filename. + """ + if self.optional and not os.path.isfile(self.filename): + value = {} + else: + value = yaml_util.load_yaml(self.filename, + loader=self.loader) or {} + self.update(value) + + +class EnvSource(ConfigSource): + """A configuration data source loaded from environment variables. + """ + def __init__(self, prefix, sep='__', lower=True, handle_lists=True, + parse_yaml_docs=False, loader=yaml_util.Loader): + """Create a configuration source from the environment. + + :param prefix: The prefix used to identify the environment variables + to be loaded into this configuration source. + + :param sep: Separator within variable names to define nested keys. + + :param lower: Indicates whether to convert variable names to lowercase + after prefix matching. + + :param handle_lists: If variables are split into nested keys, indicates + whether to search for sub-dicts with keys that are sequential + integers starting from 0 and convert those dicts to lists. + + :param parse_yaml_docs: Enable parsing the values of environment + variables as full YAML documents. By default, when False, values + are parsed only as YAML scalars. + + :param loader: PyYAML Loader class to use to parse YAML values. + """ + super(EnvSource, self).__init__({}, filename=None, default=False, + base_for_paths=False) + self.prefix = prefix + self.sep = sep + self.lower = lower + self.handle_lists = handle_lists + self.parse_yaml_docs = parse_yaml_docs + self.loader = loader + self.load() + + def load(self): + """Load configuration data from the environment. + """ + # Read config variables with prefix from the environment. + config_vars = {} + for var, value in os.environ.items(): + if var.startswith(self.prefix): + key = var[len(self.prefix):] + if self.lower: + key = key.lower() + if self.parse_yaml_docs: + # Parse the value as a YAML document, which will convert + # string representations of dicts and lists into the + # appropriate object (ie, '{foo: bar}' to {'foo': 'bar'}). + # Will raise a ConfigReadError if YAML parsing fails. + value = yaml_util.load_yaml_string(value, + 'env variable ' + var, + loader=self.loader) + else: + # Parse the value as a YAML scalar so that values are type + # converted using the same rules as the YAML Loader (ie, + # numeric string to int/float, 'true' to True, etc.). Will + # not raise a ConfigReadError. + value = yaml_util.parse_as_scalar(value, + loader=self.loader) + config_vars[key] = value + if self.sep: + # Build a nested dict, keeping keys with `None` values to allow + # environment variables to unset values from lower priority sources + config_vars = build_dict(config_vars, self.sep, keep_none=True) + if self.handle_lists: + for k, v in config_vars.items(): + config_vars[k] = self._convert_dict_lists(v) + self.update(config_vars) + + @classmethod + def _convert_dict_lists(cls, obj): + """Recursively search for dicts where all of the keys are integers + from 0 to the length of the dict, and convert them to lists. + """ + # We only deal with dictionaries + if not isinstance(obj, dict): + return obj + + # Recursively search values for additional dicts to convert to lists + for k, v in obj.items(): + obj[k] = cls._convert_dict_lists(v) + + try: + # Convert the keys to integers, mapping the ints back to the keys + int_to_key = {int(k): k for k in obj.keys()} + except (ValueError): + # Not all of the keys represent integers + return obj + try: + # For the integers from 0 to the length of the dict, try to create + # a list from the dict values using the integer to key mapping + return [obj[int_to_key[i]] for i in range(len(obj))] + except (KeyError): + # At least one integer within the range is not a key of the dict + return obj diff --git a/libs/common/confuse/templates.py b/libs/common/confuse/templates.py new file mode 100644 index 00000000..1b56b28a --- /dev/null +++ b/libs/common/confuse/templates.py @@ -0,0 +1,741 @@ +from __future__ import division, absolute_import, print_function + +import os +import re +import sys + +from . import util +from . import exceptions + +try: + import enum + SUPPORTS_ENUM = True +except ImportError: + SUPPORTS_ENUM = False + +try: + import pathlib + SUPPORTS_PATHLIB = True +except ImportError: + SUPPORTS_PATHLIB = False + +if sys.version_info >= (3, 3): + from collections import abc +else: + import collections as abc + + +REQUIRED = object() +"""A sentinel indicating that there is no default value and an exception +should be raised when the value is missing. +""" + + +class Template(object): + """A value template for configuration fields. + + The template works like a type and instructs Confuse about how to + interpret a deserialized YAML value. This includes type conversions, + providing a default value, and validating for errors. For example, a + filepath type might expand tildes and check that the file exists. + """ + def __init__(self, default=REQUIRED): + """Create a template with a given default value. + + If `default` is the sentinel `REQUIRED` (as it is by default), + then an error will be raised when a value is missing. Otherwise, + missing values will instead return `default`. + """ + self.default = default + + def __call__(self, view): + """Invoking a template on a view gets the view's value according + to the template. + """ + return self.value(view, self) + + def value(self, view, template=None): + """Get the value for a `ConfigView`. + + May raise a `NotFoundError` if the value is missing (and the + template requires it) or a `ConfigValueError` for invalid values. + """ + try: + value, _ = view.first() + return self.convert(value, view) + except exceptions.NotFoundError: + pass + + # Get default value, or raise if required. + return self.get_default_value(view.name) + + def get_default_value(self, key_name='default'): + """Get the default value to return when the value is missing. + + May raise a `NotFoundError` if the value is required. + """ + if not hasattr(self, 'default') or self.default is REQUIRED: + # The value is required. A missing value is an error. + raise exceptions.NotFoundError(u"{} not found".format(key_name)) + # The value is not required. + return self.default + + def convert(self, value, view): + """Convert the YAML-deserialized value to a value of the desired + type. + + Subclasses should override this to provide useful conversions. + May raise a `ConfigValueError` when the configuration is wrong. + """ + # Default implementation does no conversion. + return value + + def fail(self, message, view, type_error=False): + """Raise an exception indicating that a value cannot be + accepted. + + `type_error` indicates whether the error is due to a type + mismatch rather than a malformed value. In this case, a more + specific exception is raised. + """ + exc_class = ( + exceptions.ConfigTypeError if type_error + else exceptions.ConfigValueError) + raise exc_class(u'{0}: {1}'.format(view.name, message)) + + def __repr__(self): + return '{0}({1})'.format( + type(self).__name__, + '' if self.default is REQUIRED else repr(self.default), + ) + + +class Integer(Template): + """An integer configuration value template. + """ + def convert(self, value, view): + """Check that the value is an integer. Floats are rounded. + """ + if isinstance(value, int): + return value + elif isinstance(value, float): + return int(value) + else: + self.fail(u'must be a number', view, True) + + +class Number(Template): + """A numeric type: either an integer or a floating-point number. + """ + def convert(self, value, view): + """Check that the value is an int or a float. + """ + if isinstance(value, util.NUMERIC_TYPES): + return value + else: + self.fail( + u'must be numeric, not {0}'.format(type(value).__name__), + view, + True + ) + + +class MappingTemplate(Template): + """A template that uses a dictionary to specify other types for the + values for a set of keys and produce a validated `AttrDict`. + """ + def __init__(self, mapping): + """Create a template according to a dict (mapping). The + mapping's values should themselves either be Types or + convertible to Types. + """ + subtemplates = {} + for key, typ in mapping.items(): + subtemplates[key] = as_template(typ) + self.subtemplates = subtemplates + + def value(self, view, template=None): + """Get a dict with the same keys as the template and values + validated according to the value types. + """ + out = AttrDict() + for key, typ in self.subtemplates.items(): + out[key] = typ.value(view[key], self) + return out + + def __repr__(self): + return 'MappingTemplate({0})'.format(repr(self.subtemplates)) + + +class Sequence(Template): + """A template used to validate lists of similar items, + based on a given subtemplate. + """ + def __init__(self, subtemplate): + """Create a template for a list with items validated + on a given subtemplate. + """ + self.subtemplate = as_template(subtemplate) + + def value(self, view, template=None): + """Get a list of items validated against the template. + """ + out = [] + for item in view.sequence(): + out.append(self.subtemplate.value(item, self)) + return out + + def __repr__(self): + return 'Sequence({0})'.format(repr(self.subtemplate)) + + +class MappingValues(Template): + """A template used to validate mappings of similar items, + based on a given subtemplate applied to the values. + + All keys in the mapping are considered valid, but values + must pass validation by the subtemplate. Similar to the + Sequence template but for mappings. + """ + def __init__(self, subtemplate): + """Create a template for a mapping with variable keys + and item values validated on a given subtemplate. + """ + self.subtemplate = as_template(subtemplate) + + def value(self, view, template=None): + """Get a dict with the same keys as the view and the + value of each item validated against the subtemplate. + """ + out = {} + for key, item in view.items(): + out[key] = self.subtemplate.value(item, self) + return out + + def __repr__(self): + return 'MappingValues({0})'.format(repr(self.subtemplate)) + + +class String(Template): + """A string configuration value template. + """ + def __init__(self, default=REQUIRED, pattern=None, expand_vars=False): + """Create a template with the added optional `pattern` argument, + a regular expression string that the value should match. + """ + super(String, self).__init__(default) + self.pattern = pattern + self.expand_vars = expand_vars + if pattern: + self.regex = re.compile(pattern) + + def __repr__(self): + args = [] + + if self.default is not REQUIRED: + args.append(repr(self.default)) + + if self.pattern is not None: + args.append('pattern=' + repr(self.pattern)) + + return 'String({0})'.format(', '.join(args)) + + def convert(self, value, view): + """Check that the value is a string and matches the pattern. + """ + if not isinstance(value, util.BASESTRING): + self.fail(u'must be a string', view, True) + + if self.pattern and not self.regex.match(value): + self.fail( + u"must match the pattern {0}".format(self.pattern), + view + ) + + if self.expand_vars: + return os.path.expandvars(value) + else: + return value + + +class Choice(Template): + """A template that permits values from a sequence of choices. + """ + def __init__(self, choices, default=REQUIRED): + """Create a template that validates any of the values from the + iterable `choices`. + + If `choices` is a map, then the corresponding value is emitted. + Otherwise, the value itself is emitted. + + If `choices` is a `Enum`, then the enum entry with the value is + emitted. + """ + super(Choice, self).__init__(default) + self.choices = choices + + def convert(self, value, view): + """Ensure that the value is among the choices (and remap if the + choices are a mapping). + """ + if (SUPPORTS_ENUM and isinstance(self.choices, type) + and issubclass(self.choices, enum.Enum)): + try: + return self.choices(value) + except ValueError: + self.fail( + u'must be one of {0!r}, not {1!r}'.format( + [c.value for c in self.choices], value + ), + view + ) + + if value not in self.choices: + self.fail( + u'must be one of {0!r}, not {1!r}'.format( + list(self.choices), value + ), + view + ) + + if isinstance(self.choices, abc.Mapping): + return self.choices[value] + else: + return value + + def __repr__(self): + return 'Choice({0!r})'.format(self.choices) + + +class OneOf(Template): + """A template that permits values complying to one of the given templates. + """ + def __init__(self, allowed, default=REQUIRED): + super(OneOf, self).__init__(default) + self.allowed = list(allowed) + + def __repr__(self): + args = [] + + if self.allowed is not None: + args.append('allowed=' + repr(self.allowed)) + + if self.default is not REQUIRED: + args.append(repr(self.default)) + + return 'OneOf({0})'.format(', '.join(args)) + + def value(self, view, template): + self.template = template + return super(OneOf, self).value(view, template) + + def convert(self, value, view): + """Ensure that the value follows at least one template. + """ + is_mapping = isinstance(self.template, MappingTemplate) + + for candidate in self.allowed: + try: + if is_mapping: + if isinstance(candidate, Filename) and \ + candidate.relative_to: + next_template = candidate.template_with_relatives( + view, + self.template + ) + + next_template.subtemplates[view.key] = as_template( + candidate + ) + else: + next_template = MappingTemplate({view.key: candidate}) + + return view.parent.get(next_template)[view.key] + else: + return view.get(candidate) + except exceptions.ConfigTemplateError: + raise + except exceptions.ConfigError: + pass + except ValueError as exc: + raise exceptions.ConfigTemplateError(exc) + + self.fail( + u'must be one of {0}, not {1}'.format( + repr(self.allowed), repr(value) + ), + view + ) + + +class StrSeq(Template): + """A template for values that are lists of strings. + + Validates both actual YAML string lists and single strings. Strings + can optionally be split on whitespace. + """ + def __init__(self, split=True, default=REQUIRED): + """Create a new template. + + `split` indicates whether, when the underlying value is a single + string, it should be split on whitespace. Otherwise, the + resulting value is a list containing a single string. + """ + super(StrSeq, self).__init__(default) + self.split = split + + def _convert_value(self, x, view): + if isinstance(x, util.STRING): + return x + elif isinstance(x, bytes): + return x.decode('utf-8', 'ignore') + else: + self.fail(u'must be a list of strings', view, True) + + def convert(self, value, view): + if isinstance(value, bytes): + value = value.decode('utf-8', 'ignore') + + if isinstance(value, util.STRING): + if self.split: + value = value.split() + else: + value = [value] + else: + try: + value = list(value) + except TypeError: + self.fail(u'must be a whitespace-separated string or a list', + view, True) + return [self._convert_value(v, view) for v in value] + + +class Pairs(StrSeq): + """A template for ordered key-value pairs. + + This can either be given with the same syntax as for `StrSeq` (i.e. without + values), or as a list of strings and/or single-element mappings such as:: + + - key: value + - [key, value] + - key + + The result is a list of two-element tuples. If no value is provided, the + `default_value` will be returned as the second element. + """ + + def __init__(self, default_value=None): + """Create a new template. + + `default` is the dictionary value returned for items that are not + a mapping, but a single string. + """ + super(Pairs, self).__init__(split=True) + self.default_value = default_value + + def _convert_value(self, x, view): + try: + return (super(Pairs, self)._convert_value(x, view), + self.default_value) + except exceptions.ConfigTypeError: + if isinstance(x, abc.Mapping): + if len(x) != 1: + self.fail(u'must be a single-element mapping', view, True) + k, v = util.iter_first(x.items()) + elif isinstance(x, abc.Sequence): + if len(x) != 2: + self.fail(u'must be a two-element list', view, True) + k, v = x + else: + # Is this even possible? -> Likely, if some !directive cause + # YAML to parse this to some custom type. + self.fail(u'must be a single string, mapping, or a list' + u'' + str(x), + view, True) + return (super(Pairs, self)._convert_value(k, view), + super(Pairs, self)._convert_value(v, view)) + + +class Filename(Template): + """A template that validates strings as filenames. + + Filenames are returned as absolute, tilde-free paths. + + Relative paths are relative to the template's `cwd` argument + when it is specified. Otherwise, if the paths come from a file, + they will be relative to the configuration directory (see the + `config_dir` method) by default or to the base directory of the + config file if either the source has `base_for_paths` set to True + or the template has `in_source_dir` set to True. Paths from sources + without a file are relative to the current working directory. This + helps attain the expected behavior when using command-line options. + """ + def __init__(self, default=REQUIRED, cwd=None, relative_to=None, + in_app_dir=False, in_source_dir=False): + """`relative_to` is the name of a sibling value that is + being validated at the same time. + + `in_app_dir` indicates whether the path should be resolved + inside the application's config directory (even when the setting + does not come from a file). + + `in_source_dir` indicates whether the path should be resolved + relative to the directory containing the source file, if there is + one, taking precedence over the application's config directory. + """ + super(Filename, self).__init__(default) + self.cwd = cwd + self.relative_to = relative_to + self.in_app_dir = in_app_dir + self.in_source_dir = in_source_dir + + def __repr__(self): + args = [] + + if self.default is not REQUIRED: + args.append(repr(self.default)) + + if self.cwd is not None: + args.append('cwd=' + repr(self.cwd)) + + if self.relative_to is not None: + args.append('relative_to=' + repr(self.relative_to)) + + if self.in_app_dir: + args.append('in_app_dir=True') + + if self.in_source_dir: + args.append('in_source_dir=True') + + return 'Filename({0})'.format(', '.join(args)) + + def resolve_relative_to(self, view, template): + if not isinstance(template, (abc.Mapping, MappingTemplate)): + # disallow config.get(Filename(relative_to='foo')) + raise exceptions.ConfigTemplateError( + u'relative_to may only be used when getting multiple values.' + ) + + elif self.relative_to == view.key: + raise exceptions.ConfigTemplateError( + u'{0} is relative to itself'.format(view.name) + ) + + elif self.relative_to not in view.parent.keys(): + # self.relative_to is not in the config + self.fail( + ( + u'needs sibling value "{0}" to expand relative path' + ).format(self.relative_to), + view + ) + + old_template = {} + old_template.update(template.subtemplates) + + # save time by skipping MappingTemplate's init loop + next_template = MappingTemplate({}) + next_relative = self.relative_to + + # gather all the needed templates and nothing else + while next_relative is not None: + try: + # pop to avoid infinite loop because of recursive + # relative paths + rel_to_template = old_template.pop(next_relative) + except KeyError: + if next_relative in template.subtemplates: + # we encountered this config key previously + raise exceptions.ConfigTemplateError(( + u'{0} and {1} are recursively relative' + ).format(view.name, self.relative_to)) + else: + raise exceptions.ConfigTemplateError(( + u'missing template for {0}, needed to expand {1}\'s' + u'relative path' + ).format(self.relative_to, view.name)) + + next_template.subtemplates[next_relative] = rel_to_template + next_relative = rel_to_template.relative_to + + return view.parent.get(next_template)[self.relative_to] + + def value(self, view, template=None): + try: + path, source = view.first() + except exceptions.NotFoundError: + return self.get_default_value(view.name) + + if not isinstance(path, util.BASESTRING): + self.fail( + u'must be a filename, not {0}'.format(type(path).__name__), + view, + True + ) + path = os.path.expanduser(util.STRING(path)) + + if not os.path.isabs(path): + if self.cwd is not None: + # relative to the template's argument + path = os.path.join(self.cwd, path) + + elif self.relative_to is not None: + path = os.path.join( + self.resolve_relative_to(view, template), + path, + ) + + elif ((source.filename and self.in_source_dir) + or (source.base_for_paths and not self.in_app_dir)): + # relative to the directory the source file is in. + path = os.path.join(os.path.dirname(source.filename), path) + + elif source.filename or self.in_app_dir: + # From defaults: relative to the app's directory. + path = os.path.join(view.root().config_dir(), path) + + return os.path.abspath(path) + + +class Path(Filename): + """A template that validates strings as `pathlib.Path` objects. + + Filenames are parsed equivalent to the `Filename` template and then + converted to `pathlib.Path` objects. + + For Python 2 it returns the original path as returned by the `Filename` + template. + """ + def value(self, view, template=None): + value = super(Path, self).value(view, template) + if value is None: + return + import pathlib + return pathlib.Path(value) + + +class Optional(Template): + """A template that makes a subtemplate optional. + + If the value is present and not null, it must validate against the + subtemplate. However, if the value is null or missing, the template will + still validate, returning a default value. If `allow_missing` is False, + the template will not allow missing values while still permitting null. + """ + + def __init__(self, subtemplate, default=None, allow_missing=True): + self.subtemplate = as_template(subtemplate) + if default is None: + # When no default is passed, try to use the subtemplate's + # default value as the default for this template + try: + default = self.subtemplate.get_default_value() + except exceptions.NotFoundError: + pass + self.default = default + self.allow_missing = allow_missing + + def value(self, view, template=None): + try: + value, _ = view.first() + except exceptions.NotFoundError: + if self.allow_missing: + # Value is missing but not required + return self.default + # Value must be present even though it can be null. Raise an error. + raise exceptions.NotFoundError(u'{} not found'.format(view.name)) + + if value is None: + # None (ie, null) is always a valid value + return self.default + return self.subtemplate.value(view, self) + + def __repr__(self): + return 'Optional({0}, {1}, allow_missing={2})'.format( + repr(self.subtemplate), + repr(self.default), + self.allow_missing, + ) + + +class TypeTemplate(Template): + """A simple template that checks that a value is an instance of a + desired Python type. + """ + def __init__(self, typ, default=REQUIRED): + """Create a template that checks that the value is an instance + of `typ`. + """ + super(TypeTemplate, self).__init__(default) + self.typ = typ + + def convert(self, value, view): + if not isinstance(value, self.typ): + self.fail( + u'must be a {0}, not {1}'.format( + self.typ.__name__, + type(value).__name__, + ), + view, + True + ) + return value + + +class AttrDict(dict): + """A `dict` subclass that can be accessed via attributes (dot + notation) for convenience. + """ + def __getattr__(self, key): + if key in self: + return self[key] + else: + raise AttributeError(key) + + def __setattr__(self, key, value): + self[key] = value + + +def as_template(value): + """Convert a simple "shorthand" Python value to a `Template`. + """ + if isinstance(value, Template): + # If it's already a Template, pass it through. + return value + elif isinstance(value, abc.Mapping): + # Dictionaries work as templates. + return MappingTemplate(value) + elif value is int: + return Integer() + elif isinstance(value, int): + return Integer(value) + elif isinstance(value, type) and issubclass(value, util.BASESTRING): + return String() + elif isinstance(value, util.BASESTRING): + return String(value) + elif isinstance(value, set): + # convert to list to avoid hash related problems + return Choice(list(value)) + elif (SUPPORTS_ENUM and isinstance(value, type) + and issubclass(value, enum.Enum)): + return Choice(value) + elif isinstance(value, list): + return OneOf(value) + elif value is float: + return Number() + elif isinstance(value, float): + return Number(value) + elif SUPPORTS_PATHLIB and isinstance(value, pathlib.PurePath): + return Path(value) + elif value is None: + return Template(None) + elif value is REQUIRED: + return Template() + elif value is dict: + return TypeTemplate(abc.Mapping) + elif value is list: + return TypeTemplate(abc.Sequence) + elif isinstance(value, type): + return TypeTemplate(value) + else: + raise ValueError(u'cannot convert to template: {0!r}'.format(value)) diff --git a/libs/common/confuse/util.py b/libs/common/confuse/util.py new file mode 100644 index 00000000..70bd4569 --- /dev/null +++ b/libs/common/confuse/util.py @@ -0,0 +1,186 @@ +from __future__ import division, absolute_import, print_function + +import os +import sys +import argparse +import optparse +import platform +import pkgutil + + +PY3 = sys.version_info[0] == 3 +STRING = str if PY3 else unicode # noqa: F821 +BASESTRING = str if PY3 else basestring # noqa: F821 +NUMERIC_TYPES = (int, float) if PY3 else (int, float, long) # noqa: F821 + + +UNIX_DIR_FALLBACK = '~/.config' +WINDOWS_DIR_VAR = 'APPDATA' +WINDOWS_DIR_FALLBACK = '~\\AppData\\Roaming' +MAC_DIR = '~/Library/Application Support' + + +def iter_first(sequence): + """Get the first element from an iterable or raise a ValueError if + the iterator generates no values. + """ + it = iter(sequence) + try: + return next(it) + except StopIteration: + raise ValueError() + + +def namespace_to_dict(obj): + """If obj is argparse.Namespace or optparse.Values we'll return + a dict representation of it, else return the original object. + + Redefine this method if using other parsers. + + :param obj: * + :return: + :rtype: dict or * + """ + if isinstance(obj, (argparse.Namespace, optparse.Values)): + return vars(obj) + return obj + + +def build_dict(obj, sep='', keep_none=False): + """Recursively builds a dictionary from an argparse.Namespace, + optparse.Values, or dict object. + + Additionally, if `sep` is a non-empty string, the keys will be split + by `sep` and expanded into a nested dict. Keys with a `None` value + are dropped by default to avoid unsetting options but can be kept + by setting `keep_none` to `True`. + + :param obj: Namespace, Values, or dict to iterate over. Other + values will simply be returned. + :type obj: argparse.Namespace or optparse.Values or dict or * + :param sep: Separator to use for splitting properties/keys of `obj` + for expansion into nested dictionaries. + :type sep: str + :param keep_none: Whether to keep keys whose value is `None`. + :type keep_none: bool + :return: A new dictionary or the value passed if obj was not a + dict, Namespace, or Values. + :rtype: dict or * + """ + # We expect our root object to be a dict, but it may come in as + # a namespace + obj = namespace_to_dict(obj) + # We only deal with dictionaries + if not isinstance(obj, dict): + return obj + + # Get keys iterator + keys = obj.keys() if PY3 else obj.iterkeys() + if sep: + # Splitting keys by `sep` needs sorted keys to prevent parents + # from clobbering children + keys = sorted(list(keys)) + + output = {} + for key in keys: + value = obj[key] + if value is None and not keep_none: # Avoid unset options. + continue + + save_to = output + result = build_dict(value, sep, keep_none) + if sep: + # Split keys by `sep` as this signifies nesting + split = key.split(sep) + if len(split) > 1: + # The last index will be the key we assign result to + key = split.pop() + # Build the dict tree if needed and change where + # we're saving to + for child_key in split: + if child_key in save_to and \ + isinstance(save_to[child_key], dict): + save_to = save_to[child_key] + else: + # Clobber or create + save_to[child_key] = {} + save_to = save_to[child_key] + + # Save + if key in save_to: + save_to[key].update(result) + else: + save_to[key] = result + return output + + +# Config file paths, including platform-specific paths and in-package +# defaults. + +def find_package_path(name): + """Returns the path to the package containing the named module or + None if the path could not be identified (e.g., if + ``name == "__main__"``). + """ + # Based on get_root_path from Flask by Armin Ronacher. + loader = pkgutil.get_loader(name) + if loader is None or name == '__main__': + return None + + if hasattr(loader, 'get_filename'): + filepath = loader.get_filename(name) + else: + # Fall back to importing the specified module. + __import__(name) + filepath = sys.modules[name].__file__ + + return os.path.dirname(os.path.abspath(filepath)) + + +def xdg_config_dirs(): + """Returns a list of paths taken from the XDG_CONFIG_DIRS + and XDG_CONFIG_HOME environment varibables if they exist + """ + paths = [] + if 'XDG_CONFIG_HOME' in os.environ: + paths.append(os.environ['XDG_CONFIG_HOME']) + if 'XDG_CONFIG_DIRS' in os.environ: + paths.extend(os.environ['XDG_CONFIG_DIRS'].split(':')) + else: + paths.append('/etc/xdg') + paths.append('/etc') + return paths + + +def config_dirs(): + """Return a platform-specific list of candidates for user + configuration directories on the system. + + The candidates are in order of priority, from highest to lowest. The + last element is the "fallback" location to be used when no + higher-priority config file exists. + """ + paths = [] + + if platform.system() == 'Darwin': + paths.append(UNIX_DIR_FALLBACK) + paths.append(MAC_DIR) + paths.extend(xdg_config_dirs()) + + elif platform.system() == 'Windows': + paths.append(WINDOWS_DIR_FALLBACK) + if WINDOWS_DIR_VAR in os.environ: + paths.append(os.environ[WINDOWS_DIR_VAR]) + + else: + # Assume Unix. + paths.append(UNIX_DIR_FALLBACK) + paths.extend(xdg_config_dirs()) + + # Expand and deduplicate paths. + out = [] + for path in paths: + path = os.path.abspath(os.path.expanduser(path)) + if path not in out: + out.append(path) + return out diff --git a/libs/common/confuse/yaml_util.py b/libs/common/confuse/yaml_util.py new file mode 100644 index 00000000..2cb4b529 --- /dev/null +++ b/libs/common/confuse/yaml_util.py @@ -0,0 +1,228 @@ +from __future__ import division, absolute_import, print_function + +from collections import OrderedDict +import yaml +from .exceptions import ConfigReadError +from .util import BASESTRING + +# YAML loading. + + +class Loader(yaml.SafeLoader): + """A customized YAML loader. This loader deviates from the official + YAML spec in a few convenient ways: + + - All strings as are Unicode objects. + - All maps are OrderedDicts. + - Strings can begin with % without quotation. + """ + # All strings should be Unicode objects, regardless of contents. + def _construct_unicode(self, node): + return self.construct_scalar(node) + + # Use ordered dictionaries for every YAML map. + # From https://gist.github.com/844388 + def construct_yaml_map(self, node): + data = OrderedDict() + yield data + value = self.construct_mapping(node) + data.update(value) + + def construct_mapping(self, node, deep=False): + if isinstance(node, yaml.MappingNode): + self.flatten_mapping(node) + else: + raise yaml.constructor.ConstructorError( + None, None, + u'expected a mapping node, but found %s' % node.id, + node.start_mark + ) + + mapping = OrderedDict() + for key_node, value_node in node.value: + key = self.construct_object(key_node, deep=deep) + try: + hash(key) + except TypeError as exc: + raise yaml.constructor.ConstructorError( + u'while constructing a mapping', + node.start_mark, 'found unacceptable key (%s)' % exc, + key_node.start_mark + ) + value = self.construct_object(value_node, deep=deep) + mapping[key] = value + return mapping + + # Allow bare strings to begin with %. Directives are still detected. + def check_plain(self): + plain = super(Loader, self).check_plain() + return plain or self.peek() == '%' + + @staticmethod + def add_constructors(loader): + """Modify a PyYAML Loader class to add extra constructors for strings + and maps. Call this method on a custom Loader class to make it behave + like Confuse's own Loader + """ + loader.add_constructor('tag:yaml.org,2002:str', + Loader._construct_unicode) + loader.add_constructor('tag:yaml.org,2002:map', + Loader.construct_yaml_map) + loader.add_constructor('tag:yaml.org,2002:omap', + Loader.construct_yaml_map) + + +Loader.add_constructors(Loader) + + +def load_yaml(filename, loader=Loader): + """Read a YAML document from a file. If the file cannot be read or + parsed, a ConfigReadError is raised. + loader is the PyYAML Loader class to use to parse the YAML. By default, + this is Confuse's own Loader class, which is like SafeLoader with + extra constructors. + """ + try: + with open(filename, 'rb') as f: + return yaml.load(f, Loader=loader) + except (IOError, yaml.error.YAMLError) as exc: + raise ConfigReadError(filename, exc) + + +def load_yaml_string(yaml_string, name, loader=Loader): + """Read a YAML document from a string. If the string cannot be parsed, + a ConfigReadError is raised. + `yaml_string` is a string to be parsed as a YAML document. + `name` is the name to use in error messages. + `loader` is the PyYAML Loader class to use to parse the YAML. By default, + this is Confuse's own Loader class, which is like SafeLoader with + extra constructors. + """ + try: + return yaml.load(yaml_string, Loader=loader) + except yaml.error.YAMLError as exc: + raise ConfigReadError(name, exc) + + +def parse_as_scalar(value, loader=Loader): + """Parse a value as if it were a YAML scalar to perform type conversion + that is consistent with YAML documents. + `value` should be a string. Non-string inputs or strings that raise YAML + errors will be returned unchanged. + `Loader` is the PyYAML Loader class to use for parsing, defaulting to + Confuse's own Loader class. + + Examples with the default Loader: + - '1' will return 1 as an integer + - '1.0' will return 1 as a float + - 'true' will return True + - The empty string '' will return None + """ + # We only deal with strings + if not isinstance(value, BASESTRING): + return value + try: + loader = loader('') + tag = loader.resolve(yaml.ScalarNode, value, (True, False)) + node = yaml.ScalarNode(tag, value) + return loader.construct_object(node) + except yaml.error.YAMLError: + # Fallback to returning the value unchanged + return value + + +# YAML dumping. + +class Dumper(yaml.SafeDumper): + """A PyYAML Dumper that represents OrderedDicts as ordinary mappings + (in order, of course). + """ + # From http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py + def represent_mapping(self, tag, mapping, flow_style=None): + value = [] + node = yaml.MappingNode(tag, value, flow_style=flow_style) + if self.alias_key is not None: + self.represented_objects[self.alias_key] = node + best_style = False + if hasattr(mapping, 'items'): + mapping = list(mapping.items()) + for item_key, item_value in mapping: + node_key = self.represent_data(item_key) + node_value = self.represent_data(item_value) + if not (isinstance(node_key, yaml.ScalarNode) + and not node_key.style): + best_style = False + if not (isinstance(node_value, yaml.ScalarNode) + and not node_value.style): + best_style = False + value.append((node_key, node_value)) + if flow_style is None: + if self.default_flow_style is not None: + node.flow_style = self.default_flow_style + else: + node.flow_style = best_style + return node + + def represent_list(self, data): + """If a list has less than 4 items, represent it in inline style + (i.e. comma separated, within square brackets). + """ + node = super(Dumper, self).represent_list(data) + length = len(data) + if self.default_flow_style is None and length < 4: + node.flow_style = True + elif self.default_flow_style is None: + node.flow_style = False + return node + + def represent_bool(self, data): + """Represent bool as 'yes' or 'no' instead of 'true' or 'false'. + """ + if data: + value = u'yes' + else: + value = u'no' + return self.represent_scalar('tag:yaml.org,2002:bool', value) + + def represent_none(self, data): + """Represent a None value with nothing instead of 'none'. + """ + return self.represent_scalar('tag:yaml.org,2002:null', '') + + +Dumper.add_representer(OrderedDict, Dumper.represent_dict) +Dumper.add_representer(bool, Dumper.represent_bool) +Dumper.add_representer(type(None), Dumper.represent_none) +Dumper.add_representer(list, Dumper.represent_list) + + +def restore_yaml_comments(data, default_data): + """Scan default_data for comments (we include empty lines in our + definition of comments) and place them before the same keys in data. + Only works with comments that are on one or more own lines, i.e. + not next to a yaml mapping. + """ + comment_map = dict() + default_lines = iter(default_data.splitlines()) + for line in default_lines: + if not line: + comment = "\n" + elif line.startswith("#"): + comment = "{0}\n".format(line) + else: + continue + while True: + line = next(default_lines) + if line and not line.startswith("#"): + break + comment += "{0}\n".format(line) + key = line.split(':')[0].strip() + comment_map[key] = comment + out_lines = iter(data.splitlines()) + out_data = "" + for line in out_lines: + key = line.split(':')[0].strip() + if key in comment_map: + out_data += comment_map[key] + out_data += "{0}\n".format(line) + return out_data diff --git a/libs/common/jellyfish/__init__.py b/libs/common/jellyfish/__init__.py index ca124f2a..b34e4dee 100644 --- a/libs/common/jellyfish/__init__.py +++ b/libs/common/jellyfish/__init__.py @@ -1,6 +1,28 @@ +import warnings + try: - from .cjellyfish import * # noqa + from .cjellyfish import * # noqa + library = "C" except ImportError: - from ._jellyfish import * # noqa + from ._jellyfish import * # noqa + library = "Python" + + +def jaro_winkler(s1, s2, long_tolerance=False): + warnings.warn( + "the name 'jaro_winkler' is deprecated and will be removed in jellyfish 1.0, " + "for the same functionality please use jaro_winkler_similarity", + DeprecationWarning, + ) + return jaro_winkler_similarity(s1, s2, long_tolerance) # noqa + + +def jaro_distance(s1, s2): + warnings.warn( + "the jaro_distance function incorrectly returns the jaro similarity, " + "replace your usage with jaro_similarity before 1.0", + DeprecationWarning, + ) + return jaro_similarity(s1, s2) # noqa diff --git a/libs/common/jellyfish/__init__.pyi b/libs/common/jellyfish/__init__.pyi new file mode 100644 index 00000000..78a58da2 --- /dev/null +++ b/libs/common/jellyfish/__init__.pyi @@ -0,0 +1,11 @@ +def levenshtein_distance(s1: str, s2: str) -> int: ... +def jaro_similarity(s1: str, s2: str) -> float: ... +def jaro_winkler_similarity(s1: str, s2: str, long_tolerance: bool = ...) -> float: ... +def damerau_levenshtein_distance(s1: str, s2: str) -> int: ... +def soundex(s: str) -> str: ... +def hamming_distance(s1: str, s2: str) -> int: ... +def nysiis(s: str) -> str: ... +def match_rating_codex(s: str) -> str: ... +def match_rating_comparison(s1: str, s2: str) -> bool: ... +def metaphone(s: str) -> str: ... +def porter_stem(s: str) -> str: ... diff --git a/libs/common/jellyfish/_jellyfish.py b/libs/common/jellyfish/_jellyfish.py index 05dade4f..8acf3f52 100644 --- a/libs/common/jellyfish/_jellyfish.py +++ b/libs/common/jellyfish/_jellyfish.py @@ -1,18 +1,16 @@ import unicodedata from collections import defaultdict -from .compat import _range, _zip_longest, IS_PY3 +from itertools import zip_longest from .porter import Stemmer def _normalize(s): - return unicodedata.normalize('NFKD', s) + return unicodedata.normalize("NFKD", s) def _check_type(s): - if IS_PY3 and not isinstance(s, str): - raise TypeError('expected str or unicode, got %s' % type(s).__name__) - elif not IS_PY3 and not isinstance(s, unicode): - raise TypeError('expected unicode, got %s' % type(s).__name__) + if not isinstance(s, str): + raise TypeError("expected str or unicode, got %s" % type(s).__name__) def levenshtein_distance(s1, s2): @@ -21,53 +19,54 @@ def levenshtein_distance(s1, s2): if s1 == s2: return 0 - rows = len(s1)+1 - cols = len(s2)+1 + rows = len(s1) + 1 + cols = len(s2) + 1 if not s1: - return cols-1 + return cols - 1 if not s2: - return rows-1 + return rows - 1 prev = None cur = range(cols) - for r in _range(1, rows): - prev, cur = cur, [r] + [0]*(cols-1) - for c in _range(1, cols): + for r in range(1, rows): + prev, cur = cur, [r] + [0] * (cols - 1) + for c in range(1, cols): deletion = prev[c] + 1 - insertion = cur[c-1] + 1 - edit = prev[c-1] + (0 if s1[r-1] == s2[c-1] else 1) + insertion = cur[c - 1] + 1 + edit = prev[c - 1] + (0 if s1[r - 1] == s2[c - 1] else 1) cur[c] = min(edit, deletion, insertion) return cur[-1] -def _jaro_winkler(ying, yang, long_tolerance, winklerize): - _check_type(ying) - _check_type(yang) +def _jaro_winkler(s1, s2, long_tolerance, winklerize): + _check_type(s1) + _check_type(s2) - ying_len = len(ying) - yang_len = len(yang) + s1_len = len(s1) + s2_len = len(s2) - if not ying_len or not yang_len: + if not s1_len or not s2_len: return 0.0 - min_len = max(ying_len, yang_len) - search_range = (min_len // 2) - 1 + min_len = min(s1_len, s2_len) + search_range = max(s1_len, s2_len) + search_range = (search_range // 2) - 1 if search_range < 0: search_range = 0 - ying_flags = [False]*ying_len - yang_flags = [False]*yang_len + s1_flags = [False] * s1_len + s2_flags = [False] * s2_len # looking only within search range, count & flag matched pairs common_chars = 0 - for i, ying_ch in enumerate(ying): - low = i - search_range if i > search_range else 0 - hi = i + search_range if i + search_range < yang_len else yang_len - 1 - for j in _range(low, hi+1): - if not yang_flags[j] and yang[j] == ying_ch: - ying_flags[i] = yang_flags[j] = True + for i, s1_ch in enumerate(s1): + low = max(0, i - search_range) + hi = min(i + search_range, s2_len - 1) + for j in range(low, hi + 1): + if not s2_flags[j] and s2[j] == s1_ch: + s1_flags[i] = s2_flags[j] = True common_chars += 1 break @@ -77,27 +76,32 @@ def _jaro_winkler(ying, yang, long_tolerance, winklerize): # count transpositions k = trans_count = 0 - for i, ying_f in enumerate(ying_flags): - if ying_f: - for j in _range(k, yang_len): - if yang_flags[j]: + for i, s1_f in enumerate(s1_flags): + if s1_f: + for j in range(k, s2_len): + if s2_flags[j]: k = j + 1 break - if ying[i] != yang[j]: + if s1[i] != s2[j]: trans_count += 1 - trans_count /= 2 + trans_count //= 2 # adjust for similarities in nonmatched characters common_chars = float(common_chars) - weight = ((common_chars/ying_len + common_chars/yang_len + - (common_chars-trans_count) / common_chars)) / 3 + weight = ( + ( + common_chars / s1_len + + common_chars / s2_len + + (common_chars - trans_count) / common_chars + ) + ) / 3 # winkler modification: continue to boost if strings are similar - if winklerize and weight > 0.7 and ying_len > 3 and yang_len > 3: + if winklerize and weight > 0.7: # adjust for up to first 4 chars in common j = min(min_len, 4) i = 0 - while i < j and ying[i] == yang[i] and ying[i]: + while i < j and s1[i] == s2[i]: i += 1 if i: weight += i * 0.1 * (1.0 - weight) @@ -105,13 +109,27 @@ def _jaro_winkler(ying, yang, long_tolerance, winklerize): # optionally adjust for long strings # after agreeing beginning chars, at least two or more must agree and # agreed characters must be > half of remaining characters - if (long_tolerance and min_len > 4 and common_chars > i+1 and - 2 * common_chars >= min_len + i): - weight += ((1.0 - weight) * (float(common_chars-i-1) / float(ying_len+yang_len-i*2+2))) + if ( + long_tolerance + and min_len > 4 + and common_chars > i + 1 + and 2 * common_chars >= min_len + i + ): + weight += (1.0 - weight) * ( + float(common_chars - i - 1) / float(s1_len + s2_len - i * 2 + 2) + ) return weight +def jaro_similarity(s1, s2): + return _jaro_winkler(s1, s2, False, False) # noqa + + +def jaro_winkler_similarity(s1, s2, long_tolerance=False): + return _jaro_winkler(s1, s2, long_tolerance, True) # noqa + + def damerau_levenshtein_distance(s1, s2): _check_type(s1) _check_type(s2) @@ -124,41 +142,35 @@ def damerau_levenshtein_distance(s1, s2): da = defaultdict(int) # distance matrix - score = [[0]*(len2+2) for x in _range(len1+2)] + score = [[0] * (len2 + 2) for x in range(len1 + 2)] score[0][0] = infinite - for i in _range(0, len1+1): - score[i+1][0] = infinite - score[i+1][1] = i - for i in _range(0, len2+1): - score[0][i+1] = infinite - score[1][i+1] = i + for i in range(0, len1 + 1): + score[i + 1][0] = infinite + score[i + 1][1] = i + for i in range(0, len2 + 1): + score[0][i + 1] = infinite + score[1][i + 1] = i - for i in _range(1, len1+1): + for i in range(1, len1 + 1): db = 0 - for j in _range(1, len2+1): - i1 = da[s2[j-1]] + for j in range(1, len2 + 1): + i1 = da[s2[j - 1]] j1 = db cost = 1 - if s1[i-1] == s2[j-1]: + if s1[i - 1] == s2[j - 1]: cost = 0 db = j - score[i+1][j+1] = min(score[i][j] + cost, - score[i+1][j] + 1, - score[i][j+1] + 1, - score[i1][j1] + (i-i1-1) + 1 + (j-j1-1)) - da[s1[i-1]] = i + score[i + 1][j + 1] = min( + score[i][j] + cost, + score[i + 1][j] + 1, + score[i][j + 1] + 1, + score[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1), + ) + da[s1[i - 1]] = i - return score[len1+1][len2+1] - - -def jaro_distance(s1, s2): - return _jaro_winkler(s1, s2, False, False) - - -def jaro_winkler(s1, s2, long_tolerance=False): - return _jaro_winkler(s1, s2, long_tolerance, True) + return score[len1 + 1][len2 + 1] def soundex(s): @@ -166,21 +178,23 @@ def soundex(s): _check_type(s) if not s: - return '' + return "" s = _normalize(s) s = s.upper() - replacements = (('BFPV', '1'), - ('CGJKQSXZ', '2'), - ('DT', '3'), - ('L', '4'), - ('MN', '5'), - ('R', '6')) + replacements = ( + ("BFPV", "1"), + ("CGJKQSXZ", "2"), + ("DT", "3"), + ("L", "4"), + ("MN", "5"), + ("R", "6"), + ) result = [s[0]] count = 1 - # find would-be replacment for first character + # find would-be replacement for first character for lset, sub in replacements: if s[0] in lset: last = sub @@ -197,12 +211,14 @@ def soundex(s): last = sub break else: - last = None + if letter != "H" and letter != "W": + # leave last alone if middle letter is H or W + last = None if count == 4: break - result += '0'*(4-count) - return ''.join(result) + result += "0" * (4 - count) + return "".join(result) def hamming_distance(s1, s2): @@ -227,28 +243,28 @@ def nysiis(s): _check_type(s) if not s: - return '' + return "" s = s.upper() key = [] # step 1 - prefixes - if s.startswith('MAC'): - s = 'MCC' + s[3:] - elif s.startswith('KN'): + if s.startswith("MAC"): + s = "MCC" + s[3:] + elif s.startswith("KN"): s = s[1:] - elif s.startswith('K'): - s = 'C' + s[1:] - elif s.startswith(('PH', 'PF')): - s = 'FF' + s[2:] - elif s.startswith('SCH'): - s = 'SSS' + s[3:] + elif s.startswith("K"): + s = "C" + s[1:] + elif s.startswith(("PH", "PF")): + s = "FF" + s[2:] + elif s.startswith("SCH"): + s = "SSS" + s[3:] # step 2 - suffixes - if s.endswith(('IE', 'EE')): - s = s[:-2] + 'Y' - elif s.endswith(('DT', 'RT', 'RD', 'NT', 'ND')): - s = s[:-2] + 'D' + if s.endswith(("IE", "EE")): + s = s[:-2] + "Y" + elif s.endswith(("DT", "RT", "RD", "NT", "ND")): + s = s[:-2] + "D" # step 3 - first character of key comes from name key.append(s[0]) @@ -258,53 +274,57 @@ def nysiis(s): len_s = len(s) while i < len_s: ch = s[i] - if ch == 'E' and i+1 < len_s and s[i+1] == 'V': - ch = 'AF' + if ch == "E" and i + 1 < len_s and s[i + 1] == "V": + ch = "AF" i += 1 - elif ch in 'AEIOU': - ch = 'A' - elif ch == 'Q': - ch = 'G' - elif ch == 'Z': - ch = 'S' - elif ch == 'M': - ch = 'N' - elif ch == 'K': - if i+1 < len(s) and s[i+1] == 'N': - ch = 'N' + elif ch in "AEIOU": + ch = "A" + elif ch == "Q": + ch = "G" + elif ch == "Z": + ch = "S" + elif ch == "M": + ch = "N" + elif ch == "K": + if i + 1 < len(s) and s[i + 1] == "N": + ch = "N" else: - ch = 'C' - elif ch == 'S' and s[i+1:i+3] == 'CH': - ch = 'SS' + ch = "C" + elif ch == "S" and s[i + 1 : i + 3] == "CH": + ch = "SS" i += 2 - elif ch == 'P' and i+1 < len(s) and s[i+1] == 'H': - ch = 'F' + elif ch == "P" and i + 1 < len(s) and s[i + 1] == "H": + ch = "F" i += 1 - elif ch == 'H' and (s[i-1] not in 'AEIOU' or (i+1 < len(s) and s[i+1] not in 'AEIOU')): - if s[i-1] in 'AEIOU': - ch = 'A' + elif ch == "H" and ( + s[i - 1] not in "AEIOU" + or (i + 1 < len(s) and s[i + 1] not in "AEIOU") + or (i + 1 == len(s)) + ): + if s[i - 1] in "AEIOU": + ch = "A" else: - ch = s[i-1] - elif ch == 'W' and s[i-1] in 'AEIOU': - ch = s[i-1] + ch = s[i - 1] + elif ch == "W" and s[i - 1] in "AEIOU": + ch = s[i - 1] if ch[-1] != key[-1][-1]: key.append(ch) i += 1 - key = ''.join(key) + key = "".join(key) # step 5 - remove trailing S - if key.endswith('S') and key != 'S': + if key.endswith("S") and key != "S": key = key[:-1] # step 6 - replace AY w/ Y - if key.endswith('AY'): - key = key[:-2] + 'Y' + if key.endswith("AY"): + key = key[:-2] + "Y" # step 7 - remove trailing A - if key.endswith('A') and key != 'A': + if key.endswith("A") and key != "A": key = key[:-1] # step 8 was already done @@ -315,24 +335,26 @@ def nysiis(s): def match_rating_codex(s): _check_type(s) - s = s.upper() + # we ignore spaces + s = s.upper().replace(" ", "") codex = [] prev = None - for i, c in enumerate(s): - # not a space OR - # starting character & vowel + first = True + for c in s: + # starting character # or consonant not preceded by same consonant - if (c != ' ' and (i == 0 and c in 'AEIOU') or (c not in 'AEIOU' and c != prev)): + if first or (c not in "AEIOU" and c != prev): codex.append(c) prev = c + first = False # just use first/last 3 if len(codex) > 6: - return ''.join(codex[:3]+codex[-3:]) + return "".join(codex[:3] + codex[-3:]) else: - return ''.join(codex) + return "".join(codex) def match_rating_comparison(s1, s2): @@ -344,7 +366,7 @@ def match_rating_comparison(s1, s2): res2 = [] # length differs by 3 or more, no result - if abs(len1-len2) >= 3: + if abs(len1 - len2) >= 3: return None # get minimum rating based on sums of codexes @@ -359,7 +381,7 @@ def match_rating_comparison(s1, s2): min_rating = 2 # strip off common prefixes - for c1, c2 in _zip_longest(codex1, codex2): + for c1, c2 in zip_longest(codex1, codex2): if c1 != c2: if c1: res1.append(c1) @@ -367,7 +389,7 @@ def match_rating_comparison(s1, s2): res2.append(c2) unmatched_count1 = unmatched_count2 = 0 - for c1, c2 in _zip_longest(reversed(res1), reversed(res2)): + for c1, c2 in zip_longest(reversed(res1), reversed(res2)): if c1 != c2: if c1: unmatched_count1 += 1 @@ -385,112 +407,113 @@ def metaphone(s): s = _normalize(s.lower()) # skip first character if s starts with these - if s.startswith(('kn', 'gn', 'pn', 'ac', 'wr', 'ae')): + if s.startswith(("kn", "gn", "pn", "wr", "ae")): s = s[1:] i = 0 while i < len(s): c = s[i] - next = s[i+1] if i < len(s)-1 else '*****' - nextnext = s[i+2] if i < len(s)-2 else '*****' + next = s[i + 1] if i < len(s) - 1 else "*****" + nextnext = s[i + 2] if i < len(s) - 2 else "*****" # skip doubles except for cc - if c == next and c != 'c': + if c == next and c != "c": i += 1 continue - if c in 'aeiou': - if i == 0 or s[i-1] == ' ': + if c in "aeiou": + if i == 0 or s[i - 1] == " ": result.append(c) - elif c == 'b': - if (not (i != 0 and s[i-1] == 'm')) or next: - result.append('b') - elif c == 'c': - if next == 'i' and nextnext == 'a' or next == 'h': - result.append('x') + elif c == "b": + if (not (i != 0 and s[i - 1] == "m")) or next: + result.append("b") + elif c == "c": + if next == "i" and nextnext == "a" or next == "h": + result.append("x") i += 1 - elif next in 'iey': - result.append('s') + elif next in "iey": + result.append("s") i += 1 else: - result.append('k') - elif c == 'd': - if next == 'g' and nextnext in 'iey': - result.append('j') + result.append("k") + elif c == "d": + if next == "g" and nextnext in "iey": + result.append("j") i += 2 else: - result.append('t') - elif c in 'fjlmnr': + result.append("t") + elif c in "fjlmnr": result.append(c) - elif c == 'g': - if next in 'iey': - result.append('j') - elif next not in 'hn': - result.append('k') - elif next == 'h' and nextnext and nextnext not in 'aeiou': + elif c == "g": + if next in "iey": + result.append("j") + elif next == "h" and nextnext and nextnext not in "aeiou": i += 1 - elif c == 'h': - if i == 0 or next in 'aeiou' or s[i-1] not in 'aeiou': - result.append('h') - elif c == 'k': - if i == 0 or s[i-1] != 'c': - result.append('k') - elif c == 'p': - if next == 'h': - result.append('f') + elif next == "n" and not nextnext: i += 1 else: - result.append('p') - elif c == 'q': - result.append('k') - elif c == 's': - if next == 'h': - result.append('x') + result.append("k") + elif c == "h": + if i == 0 or next in "aeiou" or s[i - 1] not in "aeiou": + result.append("h") + elif c == "k": + if i == 0 or s[i - 1] != "c": + result.append("k") + elif c == "p": + if next == "h": + result.append("f") i += 1 - elif next == 'i' and nextnext in 'oa': - result.append('x') + else: + result.append("p") + elif c == "q": + result.append("k") + elif c == "s": + if next == "h": + result.append("x") + i += 1 + elif next == "i" and nextnext in "oa": + result.append("x") i += 2 else: - result.append('s') - elif c == 't': - if next == 'i' and nextnext in 'oa': - result.append('x') - elif next == 'h': - result.append('0') + result.append("s") + elif c == "t": + if next == "i" and nextnext in "oa": + result.append("x") + elif next == "h": + result.append("0") i += 1 - elif next != 'c' or nextnext != 'h': - result.append('t') - elif c == 'v': - result.append('f') - elif c == 'w': - if i == 0 and next == 'h': + elif next != "c" or nextnext != "h": + result.append("t") + elif c == "v": + result.append("f") + elif c == "w": + if i == 0 and next == "h": i += 1 - if nextnext in 'aeiou' or nextnext == '*****': - result.append('w') - elif next in 'aeiou' or next == '*****': - result.append('w') - elif c == 'x': + result.append("w") + elif next in "aeiou": + result.append("w") + elif c == "x": if i == 0: - if next == 'h' or (next == 'i' and nextnext in 'oa'): - result.append('x') + if next == "h" or (next == "i" and nextnext in "oa"): + result.append("x") else: - result.append('s') + result.append("s") else: - result.append('k') - result.append('s') - elif c == 'y': - if next in 'aeiou': - result.append('y') - elif c == 'z': - result.append('s') - elif c == ' ': - if len(result) > 0 and result[-1] != ' ': - result.append(' ') + result.append("k") + result.append("s") + elif c == "y": + if next in "aeiou": + result.append("y") + elif c == "z": + result.append("s") + elif c == " ": + if len(result) > 0 and result[-1] != " ": + result.append(" ") i += 1 - return ''.join(result).upper() + return "".join(result).upper() def porter_stem(s): diff --git a/libs/common/jellyfish/cjellyfish.cp37-win_amd64.pyd b/libs/common/jellyfish/cjellyfish.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..cc4067cfdc220c870e2806292f844abad9e6156c GIT binary patch literal 31232 zcmeHw3w%>mw)akxv}tKM1sW)b60~TY3Koh=Eocr+;RI5wPy~FnB@MLN*Q7Z-#)@Uq zD)A79xfd1Zp+#rD@ijB};uas+Qf!ML6nugB=&0kQ2DLCMMUeCT*FGn0T5<0Eec!#m z@4LS{;kUE)UVH7e*Is+=*V;QJ|He(s$QUyrs48R4fb_BR?|=MdF)%iK!i&S%^YL#? zY&N*wm{?d=UTdrIR7c8Gb&S1I(2gwtz|| z^LRa)uf<~+i{+>R$Xb9PrQ#FAib1Lsihv)cHB9nq$bm9Gz>QS6iLpL{n;0vMR6^-( zjMV|8-^SQ@O8>~G0QobxpgKV@V_nhdA?2p{JZpTwt#ROsM5eau?LdrwY>X98@s^hO zN??S$l5Ih8qziqCQz7WcLvL2 zUQ9;H6>z0xx|AMQz>y|7A!DE-ZAQM*JwG_!5S({hwVt>RL`tvJ*doU}gLBlLddz3e zperp#3Ot|I4Hj94TuQ4;`15O7-#V2R>Ev-K=QW`*9%NaE<^>a90|~@vb)M{%{CyUo z@hjMrYP}bHB~+U3QaZt0XgCbCE4U=a6?i$V2}KqJO>gs}fmCNOg(>@m33ikiCk5tN zB(>FP>~iMpTlJ-+v^#@Ni!h;HBOW_1U@}O+NNS6e)3NFc@l_g+`8j8VdvZ}lzVe<6 z8n8j-4#r-gJeTsBl=GEv&txR1k`>YY;YzdTEA6Pop}3wnBPs_)Vf!Ab^>d2^%@s(E zhkQ~rz1kvde_hYsP1*7>w9c2N%a^a%E%``~mlW6Tf_%l)NvYwIKV*ABw{LZIbad zmvNlaxXYy^wz`5Ttxl*4nov3<;m?k=8BXJF2|3Q-Tvf_AASrcena+%TF69%+_=aR` zb>*};h1o6nIY;t^*?V2e&0w6<2~?}3zUB=7Cop;uu7lhih0d#je^c%QOJA4h$qk^WNVS|p`{a$4l`f(4cx44t4_>+;Du zkVlrW4ZNi6=^;6dn=CiWn}~phQNV=CL5O^z;clW-W`-nx*dRX!#-zeH;yrqRw~g~A ztZx}_q0|4G$px*>a4Dr}Qoiv$t`=8dX2_{_xG+R*WIcCxeXpu6>I7%dk!ElTf8He- zJDkeRtrn-UN42)P$dWYM4O=;()Iq~o4S%FZoXT+3`Vi=x$^t$CT)-nr;_%R$r)j*m zW4xc$#yjPx)(TF%U$wdskrZvfDQ%LnQ5(xy`$A7lMb7%&G}y&k#aqOii*C88e?D-) zNNq6pWU^MvBFqaS%|>PU?&D77u;l*SrJQO!Bs4fNTJxyi?1)H#y0l^`m~Rl3c2}S@ zZK;^^rHgv@Ra#wK$``_d*OYfLcGFbroAZgD594mqbHWKB*?jS0#=<$y-~t295ly^P z-gO0hX){#oqf|iYBibydvQM=JfS^Lh3n?Q)4hg6?mb!Q!`gfO!MdGb!7(`KC6NLrs zd0B@Z%2Q5c?8;LF!5jW zvQy$^ORi#y%Qj%#s=FkAtI3)3=DN?qBRWhE(sUxKyTuMNW2g#~D-BLjZKv_$Zvp8S zb@xgNRFT*CsnGBO$Ry=y-kEvI>n>!Z=PSGNl@EnWeQ9=A28R3FsB@I>Qp~Ncoc24% zNI}Wq$WFOOPQaupIteE`SF+QWHwo3k8Ca3=kgWTaz8q&6qL`the`3+ae zLm9hPh(gu-e-}PGdazGa_GNTZxWzSksCCn<+Rpa0XQ-*<6}n#nsM`oqPkBE z=EjJ|7GnMuxE?|)JM|W$mCN3wRyq}ox-TT^9;~#VA|j->3+d>To*OU`@Yz_k?u!t# z5y6s{o~w}uh*ddav5}>jLznVC4L@gazD?}NwUOw~!2C{EFzH{)X<41$jcx{}#T7I! z#eNT%!!G3zFwK(E1$C*`cx;g|crK8Xqy>mdLF-i@+HAVe0g8#4qJF4|tysMa_l z!)T}BDPc;lA?Y}U#D}_S{VQ)qpQP+1qA{xVeU88&1iPpb4w$6U(SadF=1nGw`L!UK z79h!`NXf9b4alHj7Ftg;ngWCBRjonJ4ti`r1lEvrrhr?^{#D~u>l)xlu?18Y%{=`T z3hDc$U{VdlUm*$WPN>$m=jxW#mo4jcWZEMRm1}A!&kE z#?^u?*`JZc)wYx?Weqi_hICh~t!J3HBuV)xnpu{tT6ch2*Jg^o!Vo1!t^~bmEKs^G z&5m|++K65g(0*Q3*qzlv%FCpQ)1kZpos!CwcS5f329&mx7lzWVLxhypfh`k;*#1>u zl&dkaYAVV{i*yw|ixUNDGm}(gKbL_0%0dMCnxX5f04j+GAJW%S)k=JHefX2_#NWEL~}j305XXK@oXbrV+v^mi@N=g=SOB_(E(5}~_@ zy*as1eDrdAL&p*&trP?LBYi`E=X2U9drRR}g|F3&3K-(tJV@u2KHo2Kd5}L{p>+?k@lllr2WJJ?dQT#`*|h(!bj}q z$J)>9{|~mG(?++SR~1HwVvu1M|8D5L6MTe`0#4@Y3iuKcT>)W4`xOuaiB)ScC;Jfv zL=Ps!?X=jy_b*k1PFoH@ZVCLRTJcHIdc@|W18Lt*9u@|+GtrwtU zsGs=^gwa@me3!BptHL-c611MjOJaxk-fU_FsRx^GzMDWzdBonIVZgYfDSwd#U9K+7 zl(0W)AZH1gSQ%~UGF`z+8x6j6aVJgbqVkRyTwqeIr$u7h%h`5OM*W?bR~lZUX@5K9 zaw^}`)^JANv`<}Rd-*s9;OOM^tEM}vgq?zF9SgzyEf4$qQdeIGflqWp?^}fJ4KbJ5 zFz7b^JP|-LzOMe|xvAmQ=ca{Ip34d+KQ}#`_}mp?%X3$TO$|qVsp8e|`$jo)zV{8s z7E0VO{wnOq!!gs2N@|Bg*#1fVwM^dV!c=0T&EgEvf7WzoYO@JKI^FxljCRSm-{o$3 zHZxS5g(e6)^U~{snZjB0*Es6X=nO zw556v@!@d{4OFeOxuURzJgi!G+DU0`;n#TqD%@WLXQ8wS@FyW$hA|Gb`sZ6(ae`J4FB%8F`cyNMst8&q@g|_%Z-Eo9Ote}2#Xt?Z0WIE7Ek?&mC{D|nwpQ?!j25XKiTf$lqw}A``I8Cq+4xp2eK;$7f=|%=6*y1(XFkH@ zlP)?0zRP#ls`VN!s;0ZEQ73FrTVJ)6f-kp&B#o$()-Oq4|I478c&LxhTv-3XM_20K zh$Ys4UJI0Z7d4BzP(n2>%uzw>r<6zY7FTKotz3e~ZWOVYC0~Qzl2~B9$DZE$+OF3**o~&9Y5rSyZSMUkI zI-yJ0Bc*eZ-ivfrOV7{IMfo_@DX&Rb5|?601PfXb{yK}4hfaB)B08gfGatf;R*X^b zuayz4igM;#5eug`e(NLj)PD3OgdPFZ4gDw=Vtn?Yeu6oXvS3qoK5_5~jjNaXC!O~X zcHYn#=})zeL1DdXh!5~ra7CbNKAov|O*-!y4DSS@;D-SW<4=|Aq2o@zTL^6%MmaRR z<&p67p(F`o<2{?+#yb+@&ymf1qx$`ma3#(5&#Y(iw*%JK!b@t__i~QTg`1!@eAD~A z5q!TC-Jah=IMq5A4D|NY5ig8uE;QPcg0wDB|A_`0B={&*YbLLkjyc#}D$A0oA%Zq% zK$SC~!1T5q$$T_oZ?0NXxootg$vAby@Y2X26C^?V%eb(Gk)0z64E$_Y6|u zom@0cg=;x?96oD!q>U^GspUwiRu>3KV7?pS$C5#!@1tRt%>B(Teq$t6Cf#@wl{?0H zQQLht1NjP{H~+wqyd85<2VWOdtAS(*TA$=os3y{OA`BR{sUH;%8eX)mBJ@IhrAl9c1l;GI~EJMx7;x09QLjz!Ls-@<(> zG|n@i#pFjG(h)*qCUUU;W|M3V+5u2Ygod$HV2^SzALH->S1|Dv>K@Fy+}|q(lHs$- zH?}%+-tnHmH0v7{NZdefhnxf6ZzR8J5bl14Iy^X&mm^avlQKS{y-tmkaaamgna&Z$7GW8OuVJ&Rt?ikPRT*& z{`G+09gzKCSv!Tjoz&4v#QozxZ$_y=-gSfg4ZLCbP&IpzzEtb$SCN+aayd-r4PRs% zHeCW=7)X;#^>y(qXqCeaExuf2UuS@y1&I37x683Jt9+M*C)b}|$)gj)7uKJy;nC6I zG4-ccBkJ`H3n$i}E|KGAdSoNEgH(fReF_|$%2!V11EsG4R`4gXehh|UzPk6zk1Zg? zm*Fhjc@p~!6apqj*p4-*90`Zg;<);xoFLYpbqkG!;2{R*bi+gF@4HdB$AL6FHRR;8joRkfbM zeW$;#QjWJbs6sDmdQU6>q8~rl(Kr*juB!qa|n;M zdhZc-!u6}RWB%5e#cT9cLSq@sDGxo=ScFJF-LWgQ=8;^a!kpmTN5q}PaAwNtcwuLY zC_L8goun!6RS;t$n;_O7L)B+eR$E~MyNRN9nE1S*-6-`u06s9^o?mMGgofj|uFy)a zM`?ebTWFAZ%jfsp#-XccIfwZ@H*&b3XCWY398vWguIgM}&?`7mK@Z&-wHp`qOd3qA z^~-3Tm~Qf^UxmhZFjkh zvbGzSg_E2)ANYpV=h%F)?M53&(Iztc#%j!={hpR$zGHRLmW(C}%v=Rg(})@4>-eRN zCH;i4X$W^Cyo%6)Fg=5@w*VUeA4k}R(2UT6unzdY0)ByT*<~;rgqIM$LiilP3chUg z|K-OJV>B7ev2g}Ve8R9qL(*^|d4$0{a#RYAU5jp@*%WiM`PB88k+N_N3&#M~J>0d@ zPTLk*2IHWZo$nd;;`|YsLnx(7X%{ngJ(RU8W0$|rCbs1uXU?40 zYcEtB8|~SSjW#*C1K&2Z2+&TDFKqk0M2fGBdEk6L z`2&bcP&7D{*(NEt&?Gool}=>Je`}6~02#Q4Oq?dcbH6&#-)9zu-?rjvNs_LMi1^w!Ny)HScUa%<{ve79nwkcY5Gj^&2$v9j>)7t4pXd(_Sly9o1P4wJ|7 zBh~54v7(a$B{=VU)%p!m;fXxWk41k)TH9yIX`Fl?5ZVSB=BuAGY20$@i`o>Uq?|@| zDdat1s6G%U1@AH`aWH{nujZl}|5;<5)t{3uTm5&KL|Mqb!y+dH?l6U?`p?GHjq>Nr zm#;(40(nG-aRKPVW3ky1Hnvew8@OP9iIzYbL_$L)&{)1W%A zv}8=r^s&VL;%{2uXEKYx`MMGj{ z=uwCs!bg5o8UitnoY`l5CXxYE*jl%ms1F(gSdW!?XQbfl zGdKu?&VOo=oZ!sq@L`yAg-7^LEt3WR&-z3;UJ4eR2_!fIr0+BSt3}!3%z4*mL>WFp zp2RBSR62SNW3gMY=@-)&dpK(#lt-mS;3N5O>iAtcB>ZUPH|gomqVUA8F^cCi$ct>~ zHUELZ<=!<%G1ZIymPcSrsjKH<8(XqhRdcb2HT1!T@|1@4#{h9M64h>gfJ<+~q?euFJ0hQd9Y0G-AI>R%ke z8x2_Fd@1VdaO;Flp)2XgA2v~jQ{5#hZ@O~cUAG;Mpu=PYz|yp8&v(DM2^qpKUII3% z8XQtk&2GJ#=14VS@FwEsP>zQ8P@Jk9F9sK>s1*3ndS0(Dkq92wiPhIo!CkjTF~6EK zKc+M9jxcxV3?~fmo67kKjmyvlB6z+)G`u-S!%yh(o|(v`WyG%Sqa7S1`J%$~r z4b>Kl|!K| z7};3kvc=#+^!?t@2A-BLMfxnI*J$};or-a`lDNs=8t|WrS(Pv_aP;9u)#&Sc9oBc; z9l7xmDc@;QPV`nERu*KFn0aJ2*&fYrsQ@;LT0(#ixP3UI|Z{Y8gzf8!bp8M=q1jeous7T?B5bdOvam-x*joTnie1j zFZYJ^d!sacM)rUDR>|dd;uYJhs3erP?ipSVNZfe#n0R-f*5c3h35^OQYLesBkB>V!=~m z>OPj-`-5}N267X{4#$0GN}z10wZpW*r^g%{OddqR7|QMGq2j^CUqfni&At5m!A19R z(dCH)Vw>td6NBQJyr4+j9aQ=trZ4Ez8N8mFjc1FSBJyR|=}mu#NTQoA2Bq&McdG*@ zh`{{B4o9P@9O7_xKL;(=bkb*?W2i3cHGwXRW(I-mT#>nnp}S9!u?hE$L5tM+mL35R zoi#t0GS#89NAzgt5+9&tz2ASt+uNYaJ7JK#(5D!zIoop`u(+pv0PFsX=qRXkZ)g&F z4iK}%2xQ$wSw>{_&~XC@Z-h2nt_Z=3vChKw}WtigFaVFceL%y7L z?|Vb%_3_DPBHnXC%ZlR_?36asR`i-3>+RZBbb87YBAmFZy;=ySb zYqS?f;X@^QnFg)QE_fDrCqi6aW};T+yC{5UvR03dptKGo-BA00ypND{3hntEUqA#25mEgn zJ_>xPz<&eRN~BI+QK81b6JdagLdBe39tZlx!0Qc}$FbV+1UbJR$|*tPlo8Eov{t53 zH~%paZgHVA+?<8RKWOrjyNA~k7y1}E1I;}XsfJ7YzrYW5e?>@YUFZdkO#|)kka1sx z8Vq;=Su4;^c+wB>q4m5BSI2C^qss`->GL%DhokAQAE3W=D1WO)zl!j&I{%3py##sI zr2|hBK!pEakq7(&qStLabo?YA+0;GP4CPPlBf;86%wp^&J7YyUd`X8Hz!5$=e5L0R zZX+%Xqw`{M8T)Glef^KD&ypKQI^_uV;EHLdzt1ByJjHN;?ko3|`THt-R)1fu&`7UN zAmko2Dp>H#FrJi{U!Vq3LqZv7g*Njg0>2*kp7|Pmc0>uGINnmBVWyU`0vWXX+uL(7 z5LkTS8WJ{6KwJvm(3`afYsZV=wRNYe(tfAG@OZhlbe=(l15>`a?&C1r$Xe+akVRXV z1--Z!3MTGA`I1Qan2jheW*cM|u7>CYDEYrL*Znm(_cUa{@)cV0cWt@&4VZK|cJ_jt zFO-hvuic0S=YD^Vy$8ukl5z>Ra0%sU(b9e9@8A{Okr?_GZ<4V2xfT=k&@BuEU3v+W zNmfd8^U-g(RnVOMn~OP$8*1pWZ%NNXw?FVdYCG)fwQd}kq2WV#f@?Hs*(-d8E`VPm zGDFV>XU?`q(z?V-2zPFTZt6K-OTOM;Hc>+a`w^$X0CGPa&1{&? zEM6ntf;?i@rIG%UNNJ!@z1?5)ZfN{a%Ug>)po0bGP`jS9TgzD)$uWhV)^ncJa%9LF z_Cf|%G~;_Xk#D5ro{#;pQAye0|Gd`!onh5#P+m-wG#7-gMQS`!VH`-!&!t64Hit8i zX=tld1FbvbC=!Q=h)91M#%q)pVDQ>*YD4ZF(R|KwL!;LNdqW@h^0j(htdg5Z=EVza zdTur*4F_Ypb@-JI2}gnDYLMb1^!QA``=jub_Xg7MAkY&yrk9QIxB%(MDj#`obV|g& z;jVZQzwhD4Cz>ZRwS6x(zj*gK!hv0WD2V1s%2krP$l{;*@LJga!?GRM=_p(`gT|lv zUoXN|8ixi$c8q-Sz;H0sea>&q55Qj_2U&OBZuocb}0b(wXu=Xvdda8g$BmP`<`?U2L#c4N&c9 zyLSI=*9IR~PDFYAFzWY2IZ4N}8IzNx*4E?|Cvmwd8``ASJ;^-}7)k52pTc=I+yA-2 zmrh@!VTZn`vtnpnEbPi0x(-0tPQ8)d=2W@}_6X=p{G3R*pSx>%QZb|k=5zj{0Y3}l zKShJH_;}Wy(3fCBm!+L37?RD5-FtWg{P^~NGx%ps6@e5QU&D8Yc_I?v#zHM1!oBTv zp#$+qeP_bgM4<3q;KK6=B#i=8%*uf z)O+`a4q@TOt&n_eDAfW=xT*a!7^$~2Gyz$tG!-v6^;H?q?eLq2q;+5>ss~l`U-3Xe zJJHAVpZEEbnmX7f`b507Z3o{N?8Ema+P9J)o!@iY?>(iMe~ z2Fx3g5xNP@Bo*KX#`haA?8wu(lyHn{^zC#zm0ViZzEs)`^ zIC!hRL>0l=w1Z!rhYO|dvCtFLoPhWE^cI4JQ&j8iKq`lE$COEX=VyV1)|7Y25!Qq; zGm!OQbXF3KPs_qFL%tGBr=eKZAu4tpeS~`>SXHQ<4m^r^rB*RcDLRd6{W=v2RDEW@ zYLxB$HBO1D(RV3IPS!<^4=y#(3=&R>(5lvFz(SKD9Gi8Nat9tT*u`7%cp=hnH1BAS zT;M-Y;3GMXK_Z(&zlIWUufbhGL)OBcUqEz}*VjYkNY);_V;fC|hIeFDh~Re!JlXkT zyovEy?j}i{R6#iU=@hzU#*Q)28Q$dphh2Bv4l>?1E``yL&W0U4ffT*Mz^ga&_dw8< z7+%G+;JU^fq_krGj6lG=WQIm*NBkzBI?Pjnp=vwK8HiC|c9^fwW6c;2OuD^=M%QF2 z*#HP>9=szK}?e^g2W5Vr1}UF1z(2fA(gt19x9R^_`ReZn>*FI(u&f`bA4c% z1;<+p-RKqbM@M;-gSKW%{?fD=kibPRO^{1J+Jsv0N~E2THIzul)(qWd!@CL;Q#;7T z@C@p+(?y)#cecfna!i|p%pv%RrI)5X`qAWvaL$6KO7=;9PbzB3PgSiWxlnnjQXmhH zF%yx(+B22n7A+pkw`plc={y^XVOH(7Nm+3sFQ9$2IiDt z(g#TQ(5xy2&3{3>tsjD4=x-23@>|kEpJ|a8oe$;x0fnIGi5^KYO~OSP#F8vwvy{{6 zOUY{Km4s)K9tT7D%W2x2zs(RnNjjynw`*nNLfbJPg~x^#A&qIXK|`CSwS?nBcOtRP zz$>IOnEYqK%rN=iuknlUss>#{i%f+^hL&rIxX?00!pEY@hvotknvE|VsrPU6-6VB0 zZnix{kw3N`JgsQyazx@f-ucb z0NTxe#J7QK93t!hQ>?-G;&y=Lv9&q>M*I*c+uZmJHWq6ncTzJVSbSvy#@E+p)8Ao` zi<*aF0$GtQrpkq`j9tRNi7D_Qr%J{yL}T%;s~*nG*QG#8nltC1FIAbB=*nsJCMdHl z{0G8bOoqGC!yn;{h6Rc&CB^@f#s7Brs%S(|^-5u)bz`+`+JM;*zgM9gh(0qi3kJJ+E+-n82{G7%AJ;Ec|qsbao5ov&f#vCI!!LJD*C= zdKn-*61y#GKc>LM5kS+QCCK^6i{D+-NJSrNzbWARDJuJ*R(8{nvR$NwbClf$1E#u@ zX`)FVa0B2+oQAw%ox-Ey`V-|< z9%sOmmN)Em@G&W;pm+;@^_(cfahdes6zFSG@3pEht0mUJeCe}_V`G?a;;b41@RJV=h%g=^y@ctM1(p3%#R@^ ze%ogA&G2{HI?Nl9L=Pk5Uc{O~)#bPdzl%F8r*ckd-7~x8B!ANF$h8N~F9D#QO{dK; z{anw#JDLB13BJL`#Fy2QZ7u{6mX0=c(vwM~-Oc>_0sd_nPMD8*{A>RG8UH@azn|sb zFZ1s|;d|e{ZH6}Rg#BS%%2z~8`?$9~7($-5r-uOi3Aueyo&t&A6aAmy+0GvETZUil zAM}k9>2E5y3rr^uyNm}Vcc<&3gHCtbJS7EpXt~K)M(OSUw+@3*@C_5T1NysrUB*sV z7OL5EQ%^aLh3cemB8j4Z2F`10cLWy=qw*(x2RZGFAv?O|^Lz8{G#+m0F<~~^7lpQk zPQVYkiD$MAWN5^T;4Rc}xTgG^{OA}8m_w+qaZw+_aoDBg)hLUx_sTC0ZHDuYIAvnY zq6%IjYtQ-_=wt0(9(d^PpP&B^G_bEu6Qbn~4L+^IFLjup=cns%rVh{QFr>o*9X_kW z-{|me9aiY@J)Li)=`-%sVoP;s)8U`iMJP0Qw~k+@L!S;KREavhU5Dv9+@{lguEQ=J z?$qJ$bQs~YNl*W32)=8rMzLI%e}fJ~I^Oa#E#IcYX*zW1@HQRR>F@y^KB>dqI{a9N z-|5hz*E3FsSL)EGLrI4b`O~gv^)!V?V9O#c-8}?G=>M}|@z1q-m|urFV@zFxJ`3i| zcTiUCbqG@t@D_+Z71dQMi+t4;9&bsN+r!x6#dYLbW3@i7t;D-huJly-Y@RhW9=Fd^ z3Ot3yi{VQIBtk@4ErMKAy%wv-@ezpj|f3L+Ao>iWz+A^P~yy~1XoGbs|lfgB>^FJ&{X-OqaOD;NB zeNpOQ|A_A2lgpviG5P;QzS?TJs?@WF=}@l@AeYg%5rGf zR)xCB!e9|4HD%RR7()EhpZ+n9{RB3zr^f54#W*YRmBW;)SHS8t=GvIx23%RMeSDs&>p7H}?@Lp*%8u$5G-tj2U%R%wGFl-O!PSK*nwy4o9=&9uo3HQoj} z*RCk9Et}%*kCGsARXJIQ{@4GYqiSzuNk#e3$S!MZJzQJm03OWdG@+vgR^np_9=5!K zA$V9h)<7yjW%Okh`LNBB3Z?@NsXl0muNR~-N@IHDX>B#(d}vt!-*Q$0)p8ot1J&~c zAoCzrE!XliAd#ScUZYko(j~rX&IfI&C5_YI^VHT7ByzSwuHb1vo(3PD2EC6eEnlU< z<>eZpqIxBl1(1;i5SMZ{)uVBY=&anWmv^J__;99xwAhu9vhXR_VF|T`7EIuDz@Rm> z4K$FNIQ{A&`s}zCe;N*-&v3I&pI#kW>^#YOU)A8U7J`w#jY$Wer8xiFAc^e;${F~~ zpv}jB!N*cRcwZ5zqN_yT-{>%c?}z_I^7Qi`8mTz9oQHM!LOSf#p?$-->4&oYuS%za zaZG(oeT081`jq_n489Qho2PSg3tbf7mdhCb+n{NcRg}GLk@hE1Be=B8O!gUY|AIUX zhlQb7`&*+^%4r%%yMoH0zgRlaWM;Zoti<2(M4IZGlDiP@)m)Ki*z!TR8wq6@gmZGb z@%;ws`xzK6jc^dy$2WFj!eax1}q##Z+S~h{?PRwzvso6Y;XmQaYa1 zfc|}i-Bg}%U#IYN7W{?XoF^Lj1DsZ7fQAj?V@Nf#)LkjPBfFCegiP=q8_UMNmB2>q z9>&bRL{<-_zko2aIwD63Pt$#ni*S&S@(0rBSd?cd&rqI~rVQpmx+pWT>rwvd(Rd@J z^B9_rt`~oug~jbcU0jdDS#m0VbBNGQWpO>6K=n)?g15#pYnfS-VQ3yhU;Id(!DL{j zN5NN!WkP8@ODIf0-zBn|v3OvD;HsfIEvOUiL_R}48=BACXk>o}{kU<^HfCe69}*yXZVP6A=#U^$bo!z9Br= zbj#p+cv~R@{&2AwdEMaklkrh9_#i_mWI#TZ9h5(`EXw`l{C`k3D&GLtaI80`W@1?^ zD=ST6BMV`3(-T-P%1^%lj>(@wKf_II_@`!Oek+#aegk|9;gd`(>1z`+?~Y@Hd!GW~ zq35A#;?-Y{`W|&L)WwF@#n8@k;<2lUl8MVb4RUk+$Fc$&W1A6bu#cI@Fa|N!2jND> zvXSV6B)yLdAmoDxypM$hCZK;w@4TP%evYOwR3@=Xw6zR+aQkA}t)+2nS)qkp5BW_$ zhr63>b~frJdC*t(G{zo4AZNlvi-}nv4;zZ7aZ?zFdShAgB+QQpoIW8Iwn%mj=ljsS zcngaMAIlXDcx5hBa!bSl<3iwgegx;iXogZjy*lqj);bom)blQ+& zVJW2u=%*C)Q_8gD8mp=S2;1afe%40!@`&X z1Kw1|FGrfjnOCzH1I=Z&V_z*P27+6dJpw$dLyvVGE zW)@3n-ZqRy>>sqgL_bb9Ghrn9QfmsAiytg9x&=~z@!S$gI4DK%?LS;1OoRk^QdfCweh*HSs{^Dz9k=_QC`KNk6n zB;Ru}wq8-V<-pYg_jnX;-a4d#OOE1q8*s_s*BM2(5x7p!o$K>Y|NmgE{CD^N|K40w zu(oKnTwYOnLrI0~DOv;%3dYM9Cg87IejEJfMQ|xr%#o|yKCk;exV`E4cnW6LDm-FU>3q*x zav0S{YG~%1=9E;_Mp#|^!*V+Dw~$5?peWbtfmdW2R)(1V2EZdO`g~r!dUkwB+wwiW zvg*=Ey>4T{+QphTsz|I|oV#dxZgmYt%)3;Fr?O~{*W+PqyWXk=)xcGIk^j4>a&x@Z zmGjH1O3^F_{kIpBQ8sUlyQpw&jR(4bOQf7a- zNSEeS`AGgg%DJK3>yt|=7FS_bD#b3>ytt|i4#o;kDa7~GkOj;suYd;;yDBq!^hdLQ z5(80lWv>@*r2=oYn|xl|&4nH>ZWqu$5fppa=&aQf)!sSiI>=g}xs_R|>3Z4YU0dMs z;=-bmJaf75dQ;^YKjiK#WtT@`3(H^!r9k2jP?FNPPqeVS(j(TQ0UnC6%M6b4T5^VI z#IEw?-V*Oxvi4fG-bnLsId`Wma^e0&0z38~q;+_f%PUuUyb&qq=Pg{2=gOL<>D%im0hxR9;2)$uiM4G$jx&ySR^*YVvt#MymNJ}!89x>$!e zHxJ6M*YTl9{`kT9$sR4eL5Dbl4l2J%$9L%vXU;+S-8w#Vg@(tOa!`JOj&Igs(}jcc zyL7x`rH043a&UPa-=agDBM0So>G)_}pDSLc>$zTs8*~_jTsC3tESd z6WomOJH(^?j^uwNAL7fvNBa`V$M|mueZUj!LP-3n2H!_~JmS&5LGmHG5$wPd>_)hm zXaFNVLV|9DI-&vm3&Iw}qy25s{<~=39{K!y6VP{{C-@{nBK9=|-$59UIKh($mmy9t z(FR{B;so;$iV-gW+>Ai}JAxNXM1LbrP(s*9G=LGG9lJg>`Pw;sJ7vcn~aZWEoJlc0gKDEOLn}H|z zJ;LLNNBhXgcUGQ`Czrqz`~$+Lh)4UmqJ3cG3(J@c*?bzLzqdIZae^v>195s&OMb0F z#H0ONB z?*`mv*Z32>bGFV0u-O6KgQf*A;?pAd7{W&23DSRZ(S&&N9HyoKcGB2^=jZSqb4u7*QfuCgZj7#wD2tJ7tJh=4Cx@4`V_L6I_NlKVmf@eaN%PZE} zz@Vyj)+Knsbakz}%u`uXJGrvl?X9k@Ug4XJx0hF!)K*SeHT4o3TngnYa01JrAG@Mef-ImCp(`kc&hBFny1!0RsR$;RF8EF zz}_S^Eo~}pDr;KPRNu6rX-iX6)8kFeO)X8GO^2Jhn?g-}O>C=WYwA|p*7U72w%WH! nTbFJv-deVG&DQ#@8@6uQ+OoBCYu8rGqqaxE?w_Ck#~S$G6{*~3 literal 0 HcmV?d00001 diff --git a/libs/common/jellyfish/compat.py b/libs/common/jellyfish/compat.py deleted file mode 100644 index 180283d1..00000000 --- a/libs/common/jellyfish/compat.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys -import itertools - -IS_PY3 = sys.version_info[0] == 3 - -if IS_PY3: - _range = range - _zip_longest = itertools.zip_longest -else: - _range = xrange - _zip_longest = itertools.izip_longest diff --git a/libs/common/jellyfish/porter.py b/libs/common/jellyfish/porter.py index 2945b22d..1bf64d44 100644 --- a/libs/common/jellyfish/porter.py +++ b/libs/common/jellyfish/porter.py @@ -1,69 +1,84 @@ -from .compat import _range - _s2_options = { - 'a': ((['a', 't', 'i', 'o', 'n', 'a', 'l'], ['a', 't', 'e']), - (['t', 'i', 'o', 'n', 'a', 'l'], ['t', 'i', 'o', 'n'])), - 'c': ((['e', 'n', 'c', 'i'], ['e', 'n', 'c', 'e']), - (['a', 'n', 'c', 'i'], ['a', 'n', 'c', 'e']),), - 'e': ((['i', 'z', 'e', 'r'], ['i', 'z', 'e']),), - 'l': ((['b', 'l', 'i'], ['b', 'l', 'e']), - (['a', 'l', 'l', 'i'], ['a', 'l']), - (['e', 'n', 't', 'l', 'i'], ['e', 'n', 't']), - (['e', 'l', 'i'], ['e']), - (['o', 'u', 's', 'l', 'i'], ['o', 'u', 's']),), - 'o': ((['i', 'z', 'a', 't', 'i', 'o', 'n'], ['i', 'z', 'e']), - (['a', 't', 'i', 'o', 'n'], ['a', 't', 'e']), - (['a', 't', 'o', 'r'], ['a', 't', 'e']),), - 's': ((['a', 'l', 'i', 's', 'm'], ['a', 'l']), - (['i', 'v', 'e', 'n', 'e', 's', 's'], ['i', 'v', 'e']), - (['f', 'u', 'l', 'n', 'e', 's', 's'], ['f', 'u', 'l']), - (['o', 'u', 's', 'n', 'e', 's', 's'], ['o', 'u', 's']),), - 't': ((['a', 'l', 'i', 't', 'i'], ['a', 'l']), - (['i', 'v', 'i', 't', 'i'], ['i', 'v', 'e']), - (['b', 'i', 'l', 'i', 't', 'i'], ['b', 'l', 'e']),), - 'g': ((['l', 'o', 'g', 'i'], ['l', 'o', 'g']),), + "a": ( + (["a", "t", "i", "o", "n", "a", "l"], ["a", "t", "e"]), + (["t", "i", "o", "n", "a", "l"], ["t", "i", "o", "n"]), + ), + "c": ( + (["e", "n", "c", "i"], ["e", "n", "c", "e"]), + (["a", "n", "c", "i"], ["a", "n", "c", "e"]), + ), + "e": ((["i", "z", "e", "r"], ["i", "z", "e"]),), + "l": ( + (["b", "l", "i"], ["b", "l", "e"]), + (["a", "l", "l", "i"], ["a", "l"]), + (["e", "n", "t", "l", "i"], ["e", "n", "t"]), + (["e", "l", "i"], ["e"]), + (["o", "u", "s", "l", "i"], ["o", "u", "s"]), + ), + "o": ( + (["i", "z", "a", "t", "i", "o", "n"], ["i", "z", "e"]), + (["a", "t", "i", "o", "n"], ["a", "t", "e"]), + (["a", "t", "o", "r"], ["a", "t", "e"]), + ), + "s": ( + (["a", "l", "i", "s", "m"], ["a", "l"]), + (["i", "v", "e", "n", "e", "s", "s"], ["i", "v", "e"]), + (["f", "u", "l", "n", "e", "s", "s"], ["f", "u", "l"]), + (["o", "u", "s", "n", "e", "s", "s"], ["o", "u", "s"]), + ), + "t": ( + (["a", "l", "i", "t", "i"], ["a", "l"]), + (["i", "v", "i", "t", "i"], ["i", "v", "e"]), + (["b", "i", "l", "i", "t", "i"], ["b", "l", "e"]), + ), + "g": ((["l", "o", "g", "i"], ["l", "o", "g"]),), } _s3_options = { - 'e': ((['i', 'c', 'a', 't', 'e'], ['i', 'c']), - (['a', 't', 'i', 'v', 'e'], []), - (['a', 'l', 'i', 'z', 'e'], ['a', 'l']),), - 'i': ((['i', 'c', 'i', 't', 'i'], ['i', 'c']),), - 'l': ((['i', 'c', 'a', 'l'], ['i', 'c']), - (['f', 'u', 'l'], []),), - 's': ((['n', 'e', 's', 's'], []),), + "e": ( + (["i", "c", "a", "t", "e"], ["i", "c"]), + (["a", "t", "i", "v", "e"], []), + (["a", "l", "i", "z", "e"], ["a", "l"]), + ), + "i": ((["i", "c", "i", "t", "i"], ["i", "c"]),), + "l": ((["i", "c", "a", "l"], ["i", "c"]), (["f", "u", "l"], [])), + "s": ((["n", "e", "s", "s"], []),), } _s4_endings = { - 'a': (['a', 'l'],), - 'c': (['a', 'n', 'c', 'e'], ['e', 'n', 'c', 'e']), - 'e': (['e', 'r'],), - 'i': (['i', 'c'],), - 'l': (['a', 'b', 'l', 'e'], ['i', 'b', 'l', 'e']), - 'n': (['a', 'n', 't'], ['e', 'm', 'e', 'n', 't'], ['m', 'e', 'n', 't'], - ['e', 'n', 't']), + "a": (["a", "l"],), + "c": (["a", "n", "c", "e"], ["e", "n", "c", "e"]), + "e": (["e", "r"],), + "i": (["i", "c"],), + "l": (["a", "b", "l", "e"], ["i", "b", "l", "e"]), + "n": ( + ["a", "n", "t"], + ["e", "m", "e", "n", "t"], + ["m", "e", "n", "t"], + ["e", "n", "t"], + ), # handle 'o' separately - 's': (['i', 's', 'm'],), - 't': (['a', 't', 'e'], ['i', 't', 'i']), - 'u': (['o', 'u', 's'],), - 'v': (['i', 'v', 'e'],), - 'z': (['i', 'z', 'e'],), + "s": (["i", "s", "m"],), + "t": (["a", "t", "e"], ["i", "t", "i"]), + "u": (["o", "u", "s"],), + "v": (["i", "v", "e"],), + "z": (["i", "z", "e"],), } class Stemmer(object): def __init__(self, b): self.b = list(b) - self.k = len(b)-1 + self.k = len(b) - 1 self.j = 0 def cons(self, i): """ True iff b[i] is a consonant """ - if self.b[i] in 'aeiou': + if self.b[i] in "aeiou": return False - elif self.b[i] == 'y': - return True if i == 0 else not self.cons(i-1) + elif self.b[i] == "y": + return True if i == 0 else not self.cons(i - 1) return True def m(self): @@ -96,31 +111,36 @@ class Stemmer(object): def vowel_in_stem(self): """ True iff 0...j contains vowel """ - for i in _range(0, self.j+1): + for i in range(0, self.j + 1): if not self.cons(i): return True return False def doublec(self, j): """ True iff j, j-1 contains double consonant """ - if j < 1 or self.b[j] != self.b[j-1]: + if j < 1 or self.b[j] != self.b[j - 1]: return False return self.cons(j) def cvc(self, i): - """ True iff i-2,i-1,i is consonent-vowel consonant + """ True iff i-2,i-1,i is consonant-vowel consonant and if second c isn't w,x, or y. used to restore e at end of short words like cave, love, hope, crime """ - if (i < 2 or not self.cons(i) or self.cons(i-1) or not self.cons(i-2) or - self.b[i] in 'wxy'): + if ( + i < 2 + or not self.cons(i) + or self.cons(i - 1) + or not self.cons(i - 2) + or self.b[i] in "wxy" + ): return False return True def ends(self, s): length = len(s) """ True iff 0...k ends with string s """ - res = (self.b[self.k-length+1:self.k+1] == s) + res = self.b[self.k - length + 1 : self.k + 1] == s if res: self.j = self.k - length return res @@ -128,7 +148,7 @@ class Stemmer(object): def setto(self, s): """ set j+1...k to string s, readjusting k """ length = len(s) - self.b[self.j+1:self.j+1+length] = s + self.b[self.j + 1 : self.j + 1 + length] = s self.k = self.j + length def r(self, s): @@ -136,39 +156,40 @@ class Stemmer(object): self.setto(s) def step1ab(self): - if self.b[self.k] == 's': - if self.ends(['s', 's', 'e', 's']): + if self.b[self.k] == "s": + if self.ends(["s", "s", "e", "s"]): self.k -= 2 - elif self.ends(['i', 'e', 's']): - self.setto(['i']) - elif self.b[self.k-1] != 's': + elif self.ends(["i", "e", "s"]): + self.setto(["i"]) + elif self.b[self.k - 1] != "s": self.k -= 1 - if self.ends(['e', 'e', 'd']): + if self.ends(["e", "e", "d"]): if self.m() > 0: self.k -= 1 - elif ((self.ends(['e', 'd']) or self.ends(['i', 'n', 'g'])) and - self.vowel_in_stem()): + elif ( + self.ends(["e", "d"]) or self.ends(["i", "n", "g"]) + ) and self.vowel_in_stem(): self.k = self.j - if self.ends(['a', 't']): - self.setto(['a', 't', 'e']) - elif self.ends(['b', 'l']): - self.setto(['b', 'l', 'e']) - elif self.ends(['i', 'z']): - self.setto(['i', 'z', 'e']) + if self.ends(["a", "t"]): + self.setto(["a", "t", "e"]) + elif self.ends(["b", "l"]): + self.setto(["b", "l", "e"]) + elif self.ends(["i", "z"]): + self.setto(["i", "z", "e"]) elif self.doublec(self.k): self.k -= 1 - if self.b[self.k] in 'lsz': + if self.b[self.k] in "lsz": self.k += 1 elif self.m() == 1 and self.cvc(self.k): - self.setto(['e']) + self.setto(["e"]) def step1c(self): """ turn terminal y into i if there's a vowel in stem """ - if self.ends(['y']) and self.vowel_in_stem(): - self.b[self.k] = 'i' + if self.ends(["y"]) and self.vowel_in_stem(): + self.b[self.k] = "i" def step2and3(self): - for end, repl in _s2_options.get(self.b[self.k-1], []): + for end, repl in _s2_options.get(self.b[self.k - 1], []): if self.ends(end): self.r(repl) break @@ -179,11 +200,13 @@ class Stemmer(object): break def step4(self): - ch = self.b[self.k-1] + ch = self.b[self.k - 1] - if ch == 'o': - if not ((self.ends(['i', 'o', 'n']) and self.b[self.j] in 'st') or - self.ends(['o', 'u'])): + if ch == "o": + if not ( + (self.ends(["i", "o", "n"]) and self.b[self.j] in "st") + or self.ends(["o", "u"]) + ): return else: endings = _s4_endings.get(ch, []) @@ -198,15 +221,15 @@ class Stemmer(object): def step5(self): self.j = self.k - if self.b[self.k] == 'e': + if self.b[self.k] == "e": a = self.m() - if a > 1 or a == 1 and not self.cvc(self.k-1): + if a > 1 or a == 1 and not self.cvc(self.k - 1): self.k -= 1 - if self.b[self.k] == 'l' and self.doublec(self.k) and self.m() > 1: + if self.b[self.k] == "l" and self.doublec(self.k) and self.m() > 1: self.k -= 1 def result(self): - return ''.join(self.b[:self.k+1]) + return "".join(self.b[: self.k + 1]) def stem(self): if self.k > 1: diff --git a/libs/common/jellyfish/py.typed b/libs/common/jellyfish/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/common/jellyfish/test.py b/libs/common/jellyfish/test.py index dea87c25..2fec35ab 100644 --- a/libs/common/jellyfish/test.py +++ b/libs/common/jellyfish/test.py @@ -1,28 +1,24 @@ # -*- coding: utf-8 -*- -import sys -if sys.version_info[0] < 3: - import unicodecsv as csv - open_kwargs = {} -else: - import csv - open_kwargs = {'encoding': 'utf8'} +import csv import platform import pytest +open_kwargs = {"encoding": "utf8"} + def assertAlmostEqual(a, b, places=3): - assert abs(a - b) < (0.1**places) + assert abs(a - b) < (0.1 ** places) -if platform.python_implementation() == 'CPython': - implementations = ['python', 'c'] +if platform.python_implementation() == "CPython": + implementations = ["python", "c"] else: - implementations = ['python'] + implementations = ["python"] @pytest.fixture(params=implementations) def jf(request): - if request.param == 'python': + if request.param == "python": from jellyfish import _jellyfish as jf else: from jellyfish import cjellyfish as jf @@ -30,64 +26,86 @@ def jf(request): def _load_data(name): - with open('testdata/{}.csv'.format(name), **open_kwargs) as f: + with open("testdata/{}.csv".format(name), **open_kwargs) as f: for data in csv.reader(f): yield data -@pytest.mark.parametrize("s1,s2,value", _load_data('jaro_winkler'), ids=str) -def test_jaro_winkler(jf, s1, s2, value): +@pytest.mark.parametrize("s1,s2,value", _load_data("jaro_winkler"), ids=str) +def test_jaro_winkler_similarity(jf, s1, s2, value): value = float(value) - assertAlmostEqual(jf.jaro_winkler(s1, s2), value, places=3) + assertAlmostEqual(jf.jaro_winkler_similarity(s1, s2), value, places=3) -@pytest.mark.parametrize("s1,s2,value", _load_data('jaro_distance'), ids=str) -def test_jaro_distance(jf, s1, s2, value): +@pytest.mark.parametrize("s1,s2,value", _load_data("jaro_winkler_longtol"), ids=str) +def test_jaro_winkler_similarity_longtol(jf, s1, s2, value): value = float(value) - assertAlmostEqual(jf.jaro_distance(s1, s2), value, places=3) + assertAlmostEqual(jf.jaro_winkler_similarity(s1, s2, True), value, places=3) -@pytest.mark.parametrize("s1,s2,value", _load_data('hamming'), ids=str) +def test_jaro_winkler_deprecation(jf): + # backwards compatibility function + from jellyfish import jaro_winkler + + with pytest.deprecated_call(): + assert jaro_winkler("a", "a") == 1 + + +def test_jaro_distance_deprecation(): + # backwards compatibility function + from jellyfish import jaro_distance + + with pytest.deprecated_call(): + assert jaro_distance("a", "a") == 1 + + +@pytest.mark.parametrize("s1,s2,value", _load_data("jaro_distance"), ids=str) +def test_jaro_similarity(jf, s1, s2, value): + value = float(value) + assertAlmostEqual(jf.jaro_similarity(s1, s2), value, places=3) + + +@pytest.mark.parametrize("s1,s2,value", _load_data("hamming"), ids=str) def test_hamming_distance(jf, s1, s2, value): value = int(value) assert jf.hamming_distance(s1, s2) == value -@pytest.mark.parametrize("s1,s2,value", _load_data('levenshtein'), ids=str) +@pytest.mark.parametrize("s1,s2,value", _load_data("levenshtein"), ids=str) def test_levenshtein_distance(jf, s1, s2, value): value = int(value) assert jf.levenshtein_distance(s1, s2) == value -@pytest.mark.parametrize("s1,s2,value", _load_data('damerau_levenshtein'), ids=str) +@pytest.mark.parametrize("s1,s2,value", _load_data("damerau_levenshtein"), ids=str) def test_damerau_levenshtein_distance(jf, s1, s2, value): value = int(value) assert jf.damerau_levenshtein_distance(s1, s2) == value -@pytest.mark.parametrize("s1,code", _load_data('soundex'), ids=str) +@pytest.mark.parametrize("s1,code", _load_data("soundex"), ids=str) def test_soundex(jf, s1, code): assert jf.soundex(s1) == code -@pytest.mark.parametrize("s1,code", _load_data('metaphone'), ids=str) +@pytest.mark.parametrize("s1,code", _load_data("metaphone"), ids=str) def test_metaphone(jf, s1, code): assert jf.metaphone(s1) == code -@pytest.mark.parametrize("s1,s2", _load_data('nysiis'), ids=str) +@pytest.mark.parametrize("s1,s2", _load_data("nysiis"), ids=str) def test_nysiis(jf, s1, s2): assert jf.nysiis(s1) == s2 -@pytest.mark.parametrize("s1,s2", _load_data('match_rating_codex'), ids=str) +@pytest.mark.parametrize("s1,s2", _load_data("match_rating_codex"), ids=str) def test_match_rating_codex(jf, s1, s2): assert jf.match_rating_codex(s1) == s2 -@pytest.mark.parametrize("s1,s2,value", _load_data('match_rating_comparison'), ids=str) +@pytest.mark.parametrize("s1,s2,value", _load_data("match_rating_comparison"), ids=str) def test_match_rating_comparison(jf, s1, s2, value): - value = {'True': True, 'False': False, 'None': None}[value] + value = {"True": True, "False": False, "None": None}[value] assert jf.match_rating_comparison(s1, s2) is value @@ -96,117 +114,125 @@ def test_match_rating_comparison(jf, s1, s2, value): # def test_porter_stem(jf, a, b): # assert jf.porter_stem(a) == b + def test_porter_stem(jf): - with open('testdata/porter.csv', **open_kwargs) as f: + with open("testdata/porter.csv", **open_kwargs) as f: reader = csv.reader(f) for (a, b) in reader: assert jf.porter_stem(a) == b -if platform.python_implementation() == 'CPython': +if platform.python_implementation() == "CPython": + def test_match_rating_comparison_segfault(): import hashlib from jellyfish import cjellyfish as jf - sha1s = [u'{}'.format(hashlib.sha1(str(v).encode('ascii')).hexdigest()) - for v in range(100)] + + sha1s = [ + u"{}".format(hashlib.sha1(str(v).encode("ascii")).hexdigest()) + for v in range(100) + ] # this segfaulted on 0.1.2 assert [[jf.match_rating_comparison(h1, h2) for h1 in sha1s] for h2 in sha1s] def test_damerau_levenshtein_unicode_segfault(): - # unfortunate difference in behavior between Py & C versions + # test that unicode works in C & Python versions now from jellyfish.cjellyfish import damerau_levenshtein_distance as c_dl from jellyfish._jellyfish import damerau_levenshtein_distance as py_dl - s1 = u'mylifeoutdoors' - s2 = u'нахлыст' - with pytest.raises(ValueError): - c_dl(s1, s2) - with pytest.raises(ValueError): - c_dl(s2, s1) + + s1 = u"mylifeoutdoors" + s2 = u"нахлыст" + assert c_dl(s1, s2) == 14 + assert c_dl(s2, s1) == 14 assert py_dl(s1, s2) == 14 assert py_dl(s2, s1) == 14 def test_jaro_winkler_long_tolerance(jf): - no_lt = jf.jaro_winkler(u'two long strings', u'two long stringz', long_tolerance=False) - with_lt = jf.jaro_winkler(u'two long strings', u'two long stringz', long_tolerance=True) + no_lt = jf.jaro_winkler_similarity( + u"two long strings", u"two long stringz", long_tolerance=False + ) + with_lt = jf.jaro_winkler_similarity( + u"two long strings", u"two long stringz", long_tolerance=True + ) # make sure long_tolerance does something assertAlmostEqual(no_lt, 0.975) assertAlmostEqual(with_lt, 0.984) def test_damerau_levenshtein_distance_type(jf): - jf.damerau_levenshtein_distance(u'abc', u'abc') + jf.damerau_levenshtein_distance(u"abc", u"abc") with pytest.raises(TypeError) as exc: - jf.damerau_levenshtein_distance(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.damerau_levenshtein_distance(b"abc", b"abc") + assert "expected" in str(exc.value) def test_levenshtein_distance_type(jf): - assert jf.levenshtein_distance(u'abc', u'abc') == 0 + assert jf.levenshtein_distance(u"abc", u"abc") == 0 with pytest.raises(TypeError) as exc: - jf.levenshtein_distance(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.levenshtein_distance(b"abc", b"abc") + assert "expected" in str(exc.value) -def test_jaro_distance_type(jf): - assert jf.jaro_distance(u'abc', u'abc') == 1 +def test_jaro_similarity_type(jf): + assert jf.jaro_similarity(u"abc", u"abc") == 1 with pytest.raises(TypeError) as exc: - jf.jaro_distance(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.jaro_similarity(b"abc", b"abc") + assert "expected" in str(exc.value) def test_jaro_winkler_type(jf): - assert jf.jaro_winkler(u'abc', u'abc') == 1 + assert jf.jaro_winkler_similarity(u"abc", u"abc") == 1 with pytest.raises(TypeError) as exc: - jf.jaro_winkler(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.jaro_winkler_similarity(b"abc", b"abc") + assert "expected" in str(exc.value) def test_mra_comparison_type(jf): - assert jf.match_rating_comparison(u'abc', u'abc') is True + assert jf.match_rating_comparison(u"abc", u"abc") is True with pytest.raises(TypeError) as exc: - jf.match_rating_comparison(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.match_rating_comparison(b"abc", b"abc") + assert "expected" in str(exc.value) def test_hamming_type(jf): - assert jf.hamming_distance(u'abc', u'abc') == 0 + assert jf.hamming_distance(u"abc", u"abc") == 0 with pytest.raises(TypeError) as exc: - jf.hamming_distance(b'abc', b'abc') - assert 'expected' in str(exc.value) + jf.hamming_distance(b"abc", b"abc") + assert "expected" in str(exc.value) def test_soundex_type(jf): - assert jf.soundex(u'ABC') == 'A120' + assert jf.soundex(u"ABC") == "A120" with pytest.raises(TypeError) as exc: - jf.soundex(b'ABC') - assert 'expected' in str(exc.value) + jf.soundex(b"ABC") + assert "expected" in str(exc.value) def test_metaphone_type(jf): - assert jf.metaphone(u'abc') == 'ABK' + assert jf.metaphone(u"abc") == "ABK" with pytest.raises(TypeError) as exc: - jf.metaphone(b'abc') - assert 'expected' in str(exc.value) + jf.metaphone(b"abc") + assert "expected" in str(exc.value) def test_nysiis_type(jf): - assert jf.nysiis(u'abc') == 'ABC' + assert jf.nysiis(u"abc") == "ABC" with pytest.raises(TypeError) as exc: - jf.nysiis(b'abc') - assert 'expected' in str(exc.value) + jf.nysiis(b"abc") + assert "expected" in str(exc.value) def test_mr_codex_type(jf): - assert jf.match_rating_codex(u'abc') == 'ABC' + assert jf.match_rating_codex(u"abc") == "ABC" with pytest.raises(TypeError) as exc: - jf.match_rating_codex(b'abc') - assert 'expected' in str(exc.value) + jf.match_rating_codex(b"abc") + assert "expected" in str(exc.value) def test_porter_type(jf): - assert jf.porter_stem(u'abc') == 'abc' + assert jf.porter_stem(u"abc") == "abc" with pytest.raises(TypeError) as exc: - jf.porter_stem(b'abc') - assert 'expected' in str(exc.value) + jf.porter_stem(b"abc") + assert "expected" in str(exc.value) diff --git a/libs/common/mediafile.py b/libs/common/mediafile.py new file mode 100644 index 00000000..ca70c944 --- /dev/null +++ b/libs/common/mediafile.py @@ -0,0 +1,2398 @@ +# -*- coding: utf-8 -*- +# This file is part of MediaFile. +# Copyright 2016, Adrian Sampson. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Handles low-level interfacing for files' tags. Wraps Mutagen to +automatically detect file types and provide a unified interface for a +useful subset of music files' tags. + +Usage: + + >>> f = MediaFile('Lucy.mp3') + >>> f.title + u'Lucy in the Sky with Diamonds' + >>> f.artist = 'The Beatles' + >>> f.save() + +A field will always return a reasonable value of the correct type, even +if no tag is present. If no value is available, the value will be false +(e.g., zero or the empty string). + +Internally ``MediaFile`` uses ``MediaField`` descriptors to access the +data from the tags. In turn ``MediaField`` uses a number of +``StorageStyle`` strategies to handle format specific logic. +""" +from __future__ import division, absolute_import, print_function + +import mutagen +import mutagen.id3 +import mutagen.mp3 +import mutagen.mp4 +import mutagen.flac +import mutagen.asf +import mutagen._util + +import base64 +import binascii +import codecs +import datetime +import enum +import functools +import imghdr +import logging +import math +import os +import re +import six +import struct +import traceback + + +__version__ = '0.10.1' +__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile'] + +log = logging.getLogger(__name__) + +# Human-readable type names. +TYPES = { + 'mp3': 'MP3', + 'aac': 'AAC', + 'alac': 'ALAC', + 'ogg': 'OGG', + 'opus': 'Opus', + 'flac': 'FLAC', + 'ape': 'APE', + 'wv': 'WavPack', + 'mpc': 'Musepack', + 'asf': 'Windows Media', + 'aiff': 'AIFF', + 'dsf': 'DSD Stream File', + 'wav': 'WAVE', +} + +PREFERRED_IMAGE_EXTENSIONS = {'jpeg': 'jpg'} + + +# Exceptions. + +class UnreadableFileError(Exception): + """Mutagen is not able to extract information from the file. + """ + def __init__(self, filename, msg): + Exception.__init__(self, msg if msg else repr(filename)) + + +class FileTypeError(UnreadableFileError): + """Reading this type of file is not supported. + + If passed the `mutagen_type` argument this indicates that the + mutagen type is not supported by `Mediafile`. + """ + def __init__(self, filename, mutagen_type=None): + if mutagen_type is None: + msg = u'{0!r}: not in a recognized format'.format(filename) + else: + msg = u'{0}: of mutagen type {1}'.format( + repr(filename), mutagen_type + ) + Exception.__init__(self, msg) + + +class MutagenError(UnreadableFileError): + """Raised when Mutagen fails unexpectedly---probably due to a bug. + """ + def __init__(self, filename, mutagen_exc): + msg = u'{0}: {1}'.format(repr(filename), mutagen_exc) + Exception.__init__(self, msg) + + +# Interacting with Mutagen. + + +def mutagen_call(action, filename, func, *args, **kwargs): + """Call a Mutagen function with appropriate error handling. + + `action` is a string describing what the function is trying to do, + and `filename` is the relevant filename. The rest of the arguments + describe the callable to invoke. + + We require at least Mutagen 1.33, where `IOError` is *never* used, + neither for internal parsing errors *nor* for ordinary IO error + conditions such as a bad filename. Mutagen-specific parsing errors and IO + errors are reraised as `UnreadableFileError`. Other exceptions + raised inside Mutagen---i.e., bugs---are reraised as `MutagenError`. + """ + try: + return func(*args, **kwargs) + except mutagen.MutagenError as exc: + log.debug(u'%s failed: %s', action, six.text_type(exc)) + raise UnreadableFileError(filename, six.text_type(exc)) + except UnreadableFileError: + # Reraise our errors without changes. + # Used in case of decorating functions (e.g. by `loadfile`). + raise + except Exception as exc: + # Isolate bugs in Mutagen. + log.debug(u'%s', traceback.format_exc()) + log.error(u'uncaught Mutagen exception in %s: %s', action, exc) + raise MutagenError(filename, exc) + + +def loadfile(method=True, writable=False, create=False): + """A decorator that works like `mutagen._util.loadfile` but with + additional error handling. + + Opens a file and passes a `mutagen._utils.FileThing` to the + decorated function. Should be used as a decorator for functions + using a `filething` parameter. + """ + def decorator(func): + f = mutagen._util.loadfile(method, writable, create)(func) + + @functools.wraps(func) + def wrapper(*args, **kwargs): + return mutagen_call('loadfile', '', f, *args, **kwargs) + return wrapper + return decorator + + +# Utility. + +def _update_filething(filething): + """Reopen a `filething` if it's a local file. + + A filething that is *not* an actual file is left unchanged; a + filething with a filename is reopened and a new object is returned. + """ + if filething.filename: + return mutagen._util.FileThing( + None, filething.filename, filething.name + ) + else: + return filething + + +def _safe_cast(out_type, val): + """Try to covert val to out_type but never raise an exception. + + If the value does not exist, return None. Or, if the value + can't be converted, then a sensible default value is returned. + out_type should be bool, int, or unicode; otherwise, the value + is just passed through. + """ + if val is None: + return None + + if out_type == int: + if isinstance(val, int) or isinstance(val, float): + # Just a number. + return int(val) + else: + # Process any other type as a string. + if isinstance(val, bytes): + val = val.decode('utf-8', 'ignore') + elif not isinstance(val, six.string_types): + val = six.text_type(val) + # Get a number from the front of the string. + match = re.match(r'[\+-]?[0-9]+', val.strip()) + return int(match.group(0)) if match else 0 + + elif out_type == bool: + try: + # Should work for strings, bools, ints: + return bool(int(val)) + except ValueError: + return False + + elif out_type == six.text_type: + if isinstance(val, bytes): + return val.decode('utf-8', 'ignore') + elif isinstance(val, six.text_type): + return val + else: + return six.text_type(val) + + elif out_type == float: + if isinstance(val, int) or isinstance(val, float): + return float(val) + else: + if isinstance(val, bytes): + val = val.decode('utf-8', 'ignore') + else: + val = six.text_type(val) + match = re.match(r'[\+-]?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)', + val.strip()) + if match: + val = match.group(0) + if val: + return float(val) + return 0.0 + + else: + return val + + +# Image coding for ASF/WMA. + +def _unpack_asf_image(data): + """Unpack image data from a WM/Picture tag. Return a tuple + containing the MIME type, the raw image data, a type indicator, and + the image's description. + + This function is treated as "untrusted" and could throw all manner + of exceptions (out-of-bounds, etc.). We should clean this up + sometime so that the failure modes are well-defined. + """ + type, size = struct.unpack_from(' 0: + gain = math.log10(maxgain / 1000.0) * -10 + else: + # Invalid gain value found. + gain = 0.0 + + # SoundCheck stores peak values as the actual value of the sample, + # and again separately for the left and right channels. We need to + # convert this to a percentage of full scale, which is 32768 for a + # 16 bit sample. Once again, we play it safe by using the larger of + # the two values. + peak = max(soundcheck[6:8]) / 32768.0 + + return round(gain, 2), round(peak, 6) + + +def _sc_encode(gain, peak): + """Encode ReplayGain gain/peak values as a Sound Check string. + """ + # SoundCheck stores the peak value as the actual value of the + # sample, rather than the percentage of full scale that RG uses, so + # we do a simple conversion assuming 16 bit samples. + peak *= 32768.0 + + # SoundCheck stores absolute RMS values in some unknown units rather + # than the dB values RG uses. We can calculate these absolute values + # from the gain ratio using a reference value of 1000 units. We also + # enforce the maximum and minimum value here, which is equivalent to + # about -18.2dB and 30.0dB. + g1 = int(min(round((10 ** (gain / -10)) * 1000), 65534)) or 1 + # Same as above, except our reference level is 2500 units. + g2 = int(min(round((10 ** (gain / -10)) * 2500), 65534)) or 1 + + # The purpose of these values are unknown, but they also seem to be + # unused so we just use zero. + uk = 0 + values = (g1, g1, g2, g2, uk, uk, int(peak), int(peak), uk, uk) + return (u' %08X' * 10) % values + + +# Cover art and other images. +def _imghdr_what_wrapper(data): + """A wrapper around imghdr.what to account for jpeg files that can only be + identified as such using their magic bytes + See #1545 + See https://github.com/file/file/blob/master/magic/Magdir/jpeg#L12 + """ + # imghdr.what returns none for jpegs with only the magic bytes, so + # _wider_test_jpeg is run in that case. It still returns None if it didn't + # match such a jpeg file. + return imghdr.what(None, h=data) or _wider_test_jpeg(data) + + +def _wider_test_jpeg(data): + """Test for a jpeg file following the UNIX file implementation which + uses the magic bytes rather than just looking for the bytes that + represent 'JFIF' or 'EXIF' at a fixed position. + """ + if data[:2] == b'\xff\xd8': + return 'jpeg' + + +def image_mime_type(data): + """Return the MIME type of the image data (a bytestring). + """ + # This checks for a jpeg file with only the magic bytes (unrecognized by + # imghdr.what). imghdr.what returns none for that type of file, so + # _wider_test_jpeg is run in that case. It still returns None if it didn't + # match such a jpeg file. + kind = _imghdr_what_wrapper(data) + if kind in ['gif', 'jpeg', 'png', 'tiff', 'bmp']: + return 'image/{0}'.format(kind) + elif kind == 'pgm': + return 'image/x-portable-graymap' + elif kind == 'pbm': + return 'image/x-portable-bitmap' + elif kind == 'ppm': + return 'image/x-portable-pixmap' + elif kind == 'xbm': + return 'image/x-xbitmap' + else: + return 'image/x-{0}'.format(kind) + + +def image_extension(data): + ext = _imghdr_what_wrapper(data) + return PREFERRED_IMAGE_EXTENSIONS.get(ext, ext) + + +class ImageType(enum.Enum): + """Indicates the kind of an `Image` stored in a file's tag. + """ + other = 0 + icon = 1 + other_icon = 2 + front = 3 + back = 4 + leaflet = 5 + media = 6 + lead_artist = 7 + artist = 8 + conductor = 9 + group = 10 + composer = 11 + lyricist = 12 + recording_location = 13 + recording_session = 14 + performance = 15 + screen_capture = 16 + fish = 17 + illustration = 18 + artist_logo = 19 + publisher_logo = 20 + + +class Image(object): + """Structure representing image data and metadata that can be + stored and retrieved from tags. + + The structure has four properties. + * ``data`` The binary data of the image + * ``desc`` An optional description of the image + * ``type`` An instance of `ImageType` indicating the kind of image + * ``mime_type`` Read-only property that contains the mime type of + the binary data + """ + def __init__(self, data, desc=None, type=None): + assert isinstance(data, bytes) + if desc is not None: + assert isinstance(desc, six.text_type) + self.data = data + self.desc = desc + if isinstance(type, int): + try: + type = list(ImageType)[type] + except IndexError: + log.debug(u"ignoring unknown image type index %s", type) + type = ImageType.other + self.type = type + + @property + def mime_type(self): + if self.data: + return image_mime_type(self.data) + + @property + def type_index(self): + if self.type is None: + # This method is used when a tag format requires the type + # index to be set, so we return "other" as the default value. + return 0 + return self.type.value + + +# StorageStyle classes describe strategies for accessing values in +# Mutagen file objects. + +class StorageStyle(object): + """A strategy for storing a value for a certain tag format (or set + of tag formats). This basic StorageStyle describes simple 1:1 + mapping from raw values to keys in a Mutagen file object; subclasses + describe more sophisticated translations or format-specific access + strategies. + + MediaFile uses a StorageStyle via three methods: ``get()``, + ``set()``, and ``delete()``. It passes a Mutagen file object to + each. + + Internally, the StorageStyle implements ``get()`` and ``set()`` + using two steps that may be overridden by subtypes. To get a value, + the StorageStyle first calls ``fetch()`` to retrieve the value + corresponding to a key and then ``deserialize()`` to convert the raw + Mutagen value to a consumable Python value. Similarly, to set a + field, we call ``serialize()`` to encode the value and then + ``store()`` to assign the result into the Mutagen object. + + Each StorageStyle type has a class-level `formats` attribute that is + a list of strings indicating the formats that the style applies to. + MediaFile only uses StorageStyles that apply to the correct type for + a given audio file. + """ + + formats = ['FLAC', 'OggOpus', 'OggTheora', 'OggSpeex', 'OggVorbis', + 'OggFlac', 'APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio'] + """List of mutagen classes the StorageStyle can handle. + """ + + def __init__(self, key, as_type=six.text_type, suffix=None, + float_places=2, read_only=False): + """Create a basic storage strategy. Parameters: + + - `key`: The key on the Mutagen file object used to access the + field's data. + - `as_type`: The Python type that the value is stored as + internally (`unicode`, `int`, `bool`, or `bytes`). + - `suffix`: When `as_type` is a string type, append this before + storing the value. + - `float_places`: When the value is a floating-point number and + encoded as a string, the number of digits to store after the + decimal point. + - `read_only`: When true, writing to this field is disabled. + Primary use case is so wrongly named fields can be addressed + in a graceful manner. This does not block the delete method. + + """ + self.key = key + self.as_type = as_type + self.suffix = suffix + self.float_places = float_places + self.read_only = read_only + + # Convert suffix to correct string type. + if self.suffix and self.as_type is six.text_type \ + and not isinstance(self.suffix, six.text_type): + self.suffix = self.suffix.decode('utf-8') + + # Getter. + + def get(self, mutagen_file): + """Get the value for the field using this style. + """ + return self.deserialize(self.fetch(mutagen_file)) + + def fetch(self, mutagen_file): + """Retrieve the raw value of for this tag from the Mutagen file + object. + """ + try: + return mutagen_file[self.key][0] + except (KeyError, IndexError): + return None + + def deserialize(self, mutagen_value): + """Given a raw value stored on a Mutagen object, decode and + return the represented value. + """ + if self.suffix and isinstance(mutagen_value, six.text_type) \ + and mutagen_value.endswith(self.suffix): + return mutagen_value[:-len(self.suffix)] + else: + return mutagen_value + + # Setter. + + def set(self, mutagen_file, value): + """Assign the value for the field using this style. + """ + self.store(mutagen_file, self.serialize(value)) + + def store(self, mutagen_file, value): + """Store a serialized value in the Mutagen file object. + """ + mutagen_file[self.key] = [value] + + def serialize(self, value): + """Convert the external Python value to a type that is suitable for + storing in a Mutagen file object. + """ + if isinstance(value, float) and self.as_type is six.text_type: + value = u'{0:.{1}f}'.format(value, self.float_places) + value = self.as_type(value) + elif self.as_type is six.text_type: + if isinstance(value, bool): + # Store bools as 1/0 instead of True/False. + value = six.text_type(int(bool(value))) + elif isinstance(value, bytes): + value = value.decode('utf-8', 'ignore') + else: + value = six.text_type(value) + else: + value = self.as_type(value) + + if self.suffix: + value += self.suffix + + return value + + def delete(self, mutagen_file): + """Remove the tag from the file. + """ + if self.key in mutagen_file: + del mutagen_file[self.key] + + +class ListStorageStyle(StorageStyle): + """Abstract storage style that provides access to lists. + + The ListMediaField descriptor uses a ListStorageStyle via two + methods: ``get_list()`` and ``set_list()``. It passes a Mutagen file + object to each. + + Subclasses may overwrite ``fetch`` and ``store``. ``fetch`` must + return a (possibly empty) list and ``store`` receives a serialized + list of values as the second argument. + + The `serialize` and `deserialize` methods (from the base + `StorageStyle`) are still called with individual values. This class + handles packing and unpacking the values into lists. + """ + def get(self, mutagen_file): + """Get the first value in the field's value list. + """ + try: + return self.get_list(mutagen_file)[0] + except IndexError: + return None + + def get_list(self, mutagen_file): + """Get a list of all values for the field using this style. + """ + return [self.deserialize(item) for item in self.fetch(mutagen_file)] + + def fetch(self, mutagen_file): + """Get the list of raw (serialized) values. + """ + try: + return mutagen_file[self.key] + except KeyError: + return [] + + def set(self, mutagen_file, value): + """Set an individual value as the only value for the field using + this style. + """ + self.set_list(mutagen_file, [value]) + + def set_list(self, mutagen_file, values): + """Set all values for the field using this style. `values` + should be an iterable. + """ + self.store(mutagen_file, [self.serialize(value) for value in values]) + + def store(self, mutagen_file, values): + """Set the list of all raw (serialized) values for this field. + """ + mutagen_file[self.key] = values + + +class SoundCheckStorageStyleMixin(object): + """A mixin for storage styles that read and write iTunes SoundCheck + analysis values. The object must have an `index` field that + indicates which half of the gain/peak pair---0 or 1---the field + represents. + """ + def get(self, mutagen_file): + data = self.fetch(mutagen_file) + if data is not None: + return _sc_decode(data)[self.index] + + def set(self, mutagen_file, value): + data = self.fetch(mutagen_file) + if data is None: + gain_peak = [0, 0] + else: + gain_peak = list(_sc_decode(data)) + gain_peak[self.index] = value or 0 + data = self.serialize(_sc_encode(*gain_peak)) + self.store(mutagen_file, data) + + +class ASFStorageStyle(ListStorageStyle): + """A general storage style for Windows Media/ASF files. + """ + formats = ['ASF'] + + def deserialize(self, data): + if isinstance(data, mutagen.asf.ASFBaseAttribute): + data = data.value + return data + + +class MP4StorageStyle(StorageStyle): + """A general storage style for MPEG-4 tags. + """ + formats = ['MP4'] + + def serialize(self, value): + value = super(MP4StorageStyle, self).serialize(value) + if self.key.startswith('----:') and isinstance(value, six.text_type): + value = value.encode('utf-8') + return value + + +class MP4TupleStorageStyle(MP4StorageStyle): + """A style for storing values as part of a pair of numbers in an + MPEG-4 file. + """ + def __init__(self, key, index=0, **kwargs): + super(MP4TupleStorageStyle, self).__init__(key, **kwargs) + self.index = index + + def deserialize(self, mutagen_value): + items = mutagen_value or [] + packing_length = 2 + return list(items) + [0] * (packing_length - len(items)) + + def get(self, mutagen_file): + value = super(MP4TupleStorageStyle, self).get(mutagen_file)[self.index] + if value == 0: + # The values are always present and saved as integers. So we + # assume that "0" indicates it is not set. + return None + else: + return value + + def set(self, mutagen_file, value): + if value is None: + value = 0 + items = self.deserialize(self.fetch(mutagen_file)) + items[self.index] = int(value) + self.store(mutagen_file, items) + + def delete(self, mutagen_file): + if self.index == 0: + super(MP4TupleStorageStyle, self).delete(mutagen_file) + else: + self.set(mutagen_file, None) + + +class MP4ListStorageStyle(ListStorageStyle, MP4StorageStyle): + pass + + +class MP4SoundCheckStorageStyle(SoundCheckStorageStyleMixin, MP4StorageStyle): + def __init__(self, key, index=0, **kwargs): + super(MP4SoundCheckStorageStyle, self).__init__(key, **kwargs) + self.index = index + + +class MP4BoolStorageStyle(MP4StorageStyle): + """A style for booleans in MPEG-4 files. (MPEG-4 has an atom type + specifically for representing booleans.) + """ + def get(self, mutagen_file): + try: + return mutagen_file[self.key] + except KeyError: + return None + + def get_list(self, mutagen_file): + raise NotImplementedError(u'MP4 bool storage does not support lists') + + def set(self, mutagen_file, value): + mutagen_file[self.key] = value + + def set_list(self, mutagen_file, values): + raise NotImplementedError(u'MP4 bool storage does not support lists') + + +class MP4ImageStorageStyle(MP4ListStorageStyle): + """Store images as MPEG-4 image atoms. Values are `Image` objects. + """ + def __init__(self, **kwargs): + super(MP4ImageStorageStyle, self).__init__(key='covr', **kwargs) + + def deserialize(self, data): + return Image(data) + + def serialize(self, image): + if image.mime_type == 'image/png': + kind = mutagen.mp4.MP4Cover.FORMAT_PNG + elif image.mime_type == 'image/jpeg': + kind = mutagen.mp4.MP4Cover.FORMAT_JPEG + else: + raise ValueError(u'MP4 files only supports PNG and JPEG images') + return mutagen.mp4.MP4Cover(image.data, kind) + + +class MP3StorageStyle(StorageStyle): + """Store data in ID3 frames. + """ + formats = ['MP3', 'AIFF', 'DSF', 'WAVE'] + + def __init__(self, key, id3_lang=None, **kwargs): + """Create a new ID3 storage style. `id3_lang` is the value for + the language field of newly created frames. + """ + self.id3_lang = id3_lang + super(MP3StorageStyle, self).__init__(key, **kwargs) + + def fetch(self, mutagen_file): + try: + return mutagen_file[self.key].text[0] + except (KeyError, IndexError): + return None + + def store(self, mutagen_file, value): + frame = mutagen.id3.Frames[self.key](encoding=3, text=[value]) + mutagen_file.tags.setall(self.key, [frame]) + + +class MP3PeopleStorageStyle(MP3StorageStyle): + """Store list of people in ID3 frames. + """ + def __init__(self, key, involvement='', **kwargs): + self.involvement = involvement + super(MP3PeopleStorageStyle, self).__init__(key, **kwargs) + + def store(self, mutagen_file, value): + frames = mutagen_file.tags.getall(self.key) + + # Try modifying in place. + found = False + for frame in frames: + if frame.encoding == mutagen.id3.Encoding.UTF8: + for pair in frame.people: + if pair[0].lower() == self.involvement.lower(): + pair[1] = value + found = True + + # Try creating a new frame. + if not found: + frame = mutagen.id3.Frames[self.key]( + encoding=mutagen.id3.Encoding.UTF8, + people=[[self.involvement, value]] + ) + mutagen_file.tags.add(frame) + + def fetch(self, mutagen_file): + for frame in mutagen_file.tags.getall(self.key): + for pair in frame.people: + if pair[0].lower() == self.involvement.lower(): + try: + return pair[1] + except IndexError: + return None + + +class MP3ListStorageStyle(ListStorageStyle, MP3StorageStyle): + """Store lists of data in multiple ID3 frames. + """ + def fetch(self, mutagen_file): + try: + return mutagen_file[self.key].text + except KeyError: + return [] + + def store(self, mutagen_file, values): + frame = mutagen.id3.Frames[self.key](encoding=3, text=values) + mutagen_file.tags.setall(self.key, [frame]) + + +class MP3UFIDStorageStyle(MP3StorageStyle): + """Store string data in a UFID ID3 frame with a particular owner. + """ + def __init__(self, owner, **kwargs): + self.owner = owner + super(MP3UFIDStorageStyle, self).__init__('UFID:' + owner, **kwargs) + + def fetch(self, mutagen_file): + try: + return mutagen_file[self.key].data + except KeyError: + return None + + def store(self, mutagen_file, value): + # This field type stores text data as encoded data. + assert isinstance(value, six.text_type) + value = value.encode('utf-8') + + frames = mutagen_file.tags.getall(self.key) + for frame in frames: + # Replace existing frame data. + if frame.owner == self.owner: + frame.data = value + else: + # New frame. + frame = mutagen.id3.UFID(owner=self.owner, data=value) + mutagen_file.tags.setall(self.key, [frame]) + + +class MP3DescStorageStyle(MP3StorageStyle): + """Store data in a TXXX (or similar) ID3 frame. The frame is + selected based its ``desc`` field. + ``attr`` allows to specify name of data accessor property in the frame. + Most of frames use `text`. + ``multispec`` specifies if frame data is ``mutagen.id3.MultiSpec`` + which means that the data is being packed in the list. + """ + def __init__(self, desc=u'', key='TXXX', attr='text', multispec=True, + **kwargs): + assert isinstance(desc, six.text_type) + self.description = desc + self.attr = attr + self.multispec = multispec + super(MP3DescStorageStyle, self).__init__(key=key, **kwargs) + + def store(self, mutagen_file, value): + frames = mutagen_file.tags.getall(self.key) + if self.multispec: + value = [value] + + # Try modifying in place. + found = False + for frame in frames: + if frame.desc.lower() == self.description.lower(): + setattr(frame, self.attr, value) + frame.encoding = mutagen.id3.Encoding.UTF8 + found = True + + # Try creating a new frame. + if not found: + frame = mutagen.id3.Frames[self.key]( + desc=self.description, + encoding=mutagen.id3.Encoding.UTF8, + **{self.attr: value} + ) + if self.id3_lang: + frame.lang = self.id3_lang + mutagen_file.tags.add(frame) + + def fetch(self, mutagen_file): + for frame in mutagen_file.tags.getall(self.key): + if frame.desc.lower() == self.description.lower(): + if not self.multispec: + return getattr(frame, self.attr) + try: + return getattr(frame, self.attr)[0] + except IndexError: + return None + + def delete(self, mutagen_file): + found_frame = None + for frame in mutagen_file.tags.getall(self.key): + if frame.desc.lower() == self.description.lower(): + found_frame = frame + break + if found_frame is not None: + del mutagen_file[frame.HashKey] + + +class MP3ListDescStorageStyle(MP3DescStorageStyle, ListStorageStyle): + def __init__(self, desc=u'', key='TXXX', split_v23=False, **kwargs): + self.split_v23 = split_v23 + super(MP3ListDescStorageStyle, self).__init__( + desc=desc, key=key, **kwargs + ) + + def fetch(self, mutagen_file): + for frame in mutagen_file.tags.getall(self.key): + if frame.desc.lower() == self.description.lower(): + if mutagen_file.tags.version == (2, 3, 0) and self.split_v23: + return sum((el.split('/') for el in frame.text), []) + else: + return frame.text + return [] + + def store(self, mutagen_file, values): + self.delete(mutagen_file) + frame = mutagen.id3.Frames[self.key]( + desc=self.description, + text=values, + encoding=mutagen.id3.Encoding.UTF8, + ) + if self.id3_lang: + frame.lang = self.id3_lang + mutagen_file.tags.add(frame) + + +class MP3SlashPackStorageStyle(MP3StorageStyle): + """Store value as part of pair that is serialized as a slash- + separated string. + """ + def __init__(self, key, pack_pos=0, **kwargs): + super(MP3SlashPackStorageStyle, self).__init__(key, **kwargs) + self.pack_pos = pack_pos + + def _fetch_unpacked(self, mutagen_file): + data = self.fetch(mutagen_file) + if data: + items = six.text_type(data).split('/') + else: + items = [] + packing_length = 2 + return list(items) + [None] * (packing_length - len(items)) + + def get(self, mutagen_file): + return self._fetch_unpacked(mutagen_file)[self.pack_pos] + + def set(self, mutagen_file, value): + items = self._fetch_unpacked(mutagen_file) + items[self.pack_pos] = value + if items[0] is None: + items[0] = '' + if items[1] is None: + items.pop() # Do not store last value + self.store(mutagen_file, '/'.join(map(six.text_type, items))) + + def delete(self, mutagen_file): + if self.pack_pos == 0: + super(MP3SlashPackStorageStyle, self).delete(mutagen_file) + else: + self.set(mutagen_file, None) + + +class MP3ImageStorageStyle(ListStorageStyle, MP3StorageStyle): + """Converts between APIC frames and ``Image`` instances. + + The `get_list` method inherited from ``ListStorageStyle`` returns a + list of ``Image``s. Similarly, the `set_list` method accepts a + list of ``Image``s as its ``values`` argument. + """ + def __init__(self): + super(MP3ImageStorageStyle, self).__init__(key='APIC') + self.as_type = bytes + + def deserialize(self, apic_frame): + """Convert APIC frame into Image.""" + return Image(data=apic_frame.data, desc=apic_frame.desc, + type=apic_frame.type) + + def fetch(self, mutagen_file): + return mutagen_file.tags.getall(self.key) + + def store(self, mutagen_file, frames): + mutagen_file.tags.setall(self.key, frames) + + def delete(self, mutagen_file): + mutagen_file.tags.delall(self.key) + + def serialize(self, image): + """Return an APIC frame populated with data from ``image``. + """ + assert isinstance(image, Image) + frame = mutagen.id3.Frames[self.key]() + frame.data = image.data + frame.mime = image.mime_type + frame.desc = image.desc or u'' + + # For compatibility with OS X/iTunes prefer latin-1 if possible. + # See issue #899 + try: + frame.desc.encode("latin-1") + except UnicodeEncodeError: + frame.encoding = mutagen.id3.Encoding.UTF16 + else: + frame.encoding = mutagen.id3.Encoding.LATIN1 + + frame.type = image.type_index + return frame + + +class MP3SoundCheckStorageStyle(SoundCheckStorageStyleMixin, + MP3DescStorageStyle): + def __init__(self, index=0, **kwargs): + super(MP3SoundCheckStorageStyle, self).__init__(**kwargs) + self.index = index + + +class ASFImageStorageStyle(ListStorageStyle): + """Store images packed into Windows Media/ASF byte array attributes. + Values are `Image` objects. + """ + formats = ['ASF'] + + def __init__(self): + super(ASFImageStorageStyle, self).__init__(key='WM/Picture') + + def deserialize(self, asf_picture): + mime, data, type, desc = _unpack_asf_image(asf_picture.value) + return Image(data, desc=desc, type=type) + + def serialize(self, image): + pic = mutagen.asf.ASFByteArrayAttribute() + pic.value = _pack_asf_image(image.mime_type, image.data, + type=image.type_index, + description=image.desc or u'') + return pic + + +class VorbisImageStorageStyle(ListStorageStyle): + """Store images in Vorbis comments. Both legacy COVERART fields and + modern METADATA_BLOCK_PICTURE tags are supported. Data is + base64-encoded. Values are `Image` objects. + """ + formats = ['OggOpus', 'OggTheora', 'OggSpeex', 'OggVorbis', + 'OggFlac'] + + def __init__(self): + super(VorbisImageStorageStyle, self).__init__( + key='metadata_block_picture' + ) + self.as_type = bytes + + def fetch(self, mutagen_file): + images = [] + if 'metadata_block_picture' not in mutagen_file: + # Try legacy COVERART tags. + if 'coverart' in mutagen_file: + for data in mutagen_file['coverart']: + images.append(Image(base64.b64decode(data))) + return images + for data in mutagen_file["metadata_block_picture"]: + try: + pic = mutagen.flac.Picture(base64.b64decode(data)) + except (TypeError, AttributeError): + continue + images.append(Image(data=pic.data, desc=pic.desc, + type=pic.type)) + return images + + def store(self, mutagen_file, image_data): + # Strip all art, including legacy COVERART. + if 'coverart' in mutagen_file: + del mutagen_file['coverart'] + if 'coverartmime' in mutagen_file: + del mutagen_file['coverartmime'] + super(VorbisImageStorageStyle, self).store(mutagen_file, image_data) + + def serialize(self, image): + """Turn a Image into a base64 encoded FLAC picture block. + """ + pic = mutagen.flac.Picture() + pic.data = image.data + pic.type = image.type_index + pic.mime = image.mime_type + pic.desc = image.desc or u'' + + # Encoding with base64 returns bytes on both Python 2 and 3. + # Mutagen requires the data to be a Unicode string, so we decode + # it before passing it along. + return base64.b64encode(pic.write()).decode('ascii') + + +class FlacImageStorageStyle(ListStorageStyle): + """Converts between ``mutagen.flac.Picture`` and ``Image`` instances. + """ + formats = ['FLAC'] + + def __init__(self): + super(FlacImageStorageStyle, self).__init__(key='') + + def fetch(self, mutagen_file): + return mutagen_file.pictures + + def deserialize(self, flac_picture): + return Image(data=flac_picture.data, desc=flac_picture.desc, + type=flac_picture.type) + + def store(self, mutagen_file, pictures): + """``pictures`` is a list of mutagen.flac.Picture instances. + """ + mutagen_file.clear_pictures() + for pic in pictures: + mutagen_file.add_picture(pic) + + def serialize(self, image): + """Turn a Image into a mutagen.flac.Picture. + """ + pic = mutagen.flac.Picture() + pic.data = image.data + pic.type = image.type_index + pic.mime = image.mime_type + pic.desc = image.desc or u'' + return pic + + def delete(self, mutagen_file): + """Remove all images from the file. + """ + mutagen_file.clear_pictures() + + +class APEv2ImageStorageStyle(ListStorageStyle): + """Store images in APEv2 tags. Values are `Image` objects. + """ + formats = ['APEv2File', 'WavPack', 'Musepack', 'MonkeysAudio', 'OptimFROG'] + + TAG_NAMES = { + ImageType.other: 'Cover Art (other)', + ImageType.icon: 'Cover Art (icon)', + ImageType.other_icon: 'Cover Art (other icon)', + ImageType.front: 'Cover Art (front)', + ImageType.back: 'Cover Art (back)', + ImageType.leaflet: 'Cover Art (leaflet)', + ImageType.media: 'Cover Art (media)', + ImageType.lead_artist: 'Cover Art (lead)', + ImageType.artist: 'Cover Art (artist)', + ImageType.conductor: 'Cover Art (conductor)', + ImageType.group: 'Cover Art (band)', + ImageType.composer: 'Cover Art (composer)', + ImageType.lyricist: 'Cover Art (lyricist)', + ImageType.recording_location: 'Cover Art (studio)', + ImageType.recording_session: 'Cover Art (recording)', + ImageType.performance: 'Cover Art (performance)', + ImageType.screen_capture: 'Cover Art (movie scene)', + ImageType.fish: 'Cover Art (colored fish)', + ImageType.illustration: 'Cover Art (illustration)', + ImageType.artist_logo: 'Cover Art (band logo)', + ImageType.publisher_logo: 'Cover Art (publisher logo)', + } + + def __init__(self): + super(APEv2ImageStorageStyle, self).__init__(key='') + + def fetch(self, mutagen_file): + images = [] + for cover_type, cover_tag in self.TAG_NAMES.items(): + try: + frame = mutagen_file[cover_tag] + text_delimiter_index = frame.value.find(b'\x00') + if text_delimiter_index > 0: + comment = frame.value[0:text_delimiter_index] + comment = comment.decode('utf-8', 'replace') + else: + comment = None + image_data = frame.value[text_delimiter_index + 1:] + images.append(Image(data=image_data, type=cover_type, + desc=comment)) + except KeyError: + pass + + return images + + def set_list(self, mutagen_file, values): + self.delete(mutagen_file) + + for image in values: + image_type = image.type or ImageType.other + comment = image.desc or '' + image_data = comment.encode('utf-8') + b'\x00' + image.data + cover_tag = self.TAG_NAMES[image_type] + mutagen_file[cover_tag] = image_data + + def delete(self, mutagen_file): + """Remove all images from the file. + """ + for cover_tag in self.TAG_NAMES.values(): + try: + del mutagen_file[cover_tag] + except KeyError: + pass + + +# MediaField is a descriptor that represents a single logical field. It +# aggregates several StorageStyles describing how to access the data for +# each file type. + +class MediaField(object): + """A descriptor providing access to a particular (abstract) metadata + field. + """ + def __init__(self, *styles, **kwargs): + """Creates a new MediaField. + + :param styles: `StorageStyle` instances that describe the strategy + for reading and writing the field in particular + formats. There must be at least one style for + each possible file format. + + :param out_type: the type of the value that should be returned when + getting this property. + + """ + self.out_type = kwargs.get('out_type', six.text_type) + self._styles = styles + + def styles(self, mutagen_file): + """Yields the list of storage styles of this field that can + handle the MediaFile's format. + """ + for style in self._styles: + if mutagen_file.__class__.__name__ in style.formats: + yield style + + def __get__(self, mediafile, owner=None): + out = None + for style in self.styles(mediafile.mgfile): + out = style.get(mediafile.mgfile) + if out: + break + return _safe_cast(self.out_type, out) + + def __set__(self, mediafile, value): + if value is None: + value = self._none_value() + for style in self.styles(mediafile.mgfile): + if not style.read_only: + style.set(mediafile.mgfile, value) + + def __delete__(self, mediafile): + for style in self.styles(mediafile.mgfile): + style.delete(mediafile.mgfile) + + def _none_value(self): + """Get an appropriate "null" value for this field's type. This + is used internally when setting the field to None. + """ + if self.out_type == int: + return 0 + elif self.out_type == float: + return 0.0 + elif self.out_type == bool: + return False + elif self.out_type == six.text_type: + return u'' + + +class ListMediaField(MediaField): + """Property descriptor that retrieves a list of multiple values from + a tag. + + Uses ``get_list`` and set_list`` methods of its ``StorageStyle`` + strategies to do the actual work. + """ + def __get__(self, mediafile, _=None): + for style in self.styles(mediafile.mgfile): + values = style.get_list(mediafile.mgfile) + if values: + return [_safe_cast(self.out_type, value) for value in values] + return [] + + def __set__(self, mediafile, values): + for style in self.styles(mediafile.mgfile): + if not style.read_only: + style.set_list(mediafile.mgfile, values) + + def single_field(self): + """Returns a ``MediaField`` descriptor that gets and sets the + first item. + """ + options = {'out_type': self.out_type} + return MediaField(*self._styles, **options) + + +class DateField(MediaField): + """Descriptor that handles serializing and deserializing dates + + The getter parses value from tags into a ``datetime.date`` instance + and setter serializes such an instance into a string. + + For granular access to year, month, and day, use the ``*_field`` + methods to create corresponding `DateItemField`s. + """ + def __init__(self, *date_styles, **kwargs): + """``date_styles`` is a list of ``StorageStyle``s to store and + retrieve the whole date from. The ``year`` option is an + additional list of fallback styles for the year. The year is + always set on this style, but is only retrieved if the main + storage styles do not return a value. + """ + super(DateField, self).__init__(*date_styles) + year_style = kwargs.get('year', None) + if year_style: + self._year_field = MediaField(*year_style) + + def __get__(self, mediafile, owner=None): + year, month, day = self._get_date_tuple(mediafile) + if not year: + return None + try: + return datetime.date( + year, + month or 1, + day or 1 + ) + except ValueError: # Out of range values. + return None + + def __set__(self, mediafile, date): + if date is None: + self._set_date_tuple(mediafile, None, None, None) + else: + self._set_date_tuple(mediafile, date.year, date.month, date.day) + + def __delete__(self, mediafile): + super(DateField, self).__delete__(mediafile) + if hasattr(self, '_year_field'): + self._year_field.__delete__(mediafile) + + def _get_date_tuple(self, mediafile): + """Get a 3-item sequence representing the date consisting of a + year, month, and day number. Each number is either an integer or + None. + """ + # Get the underlying data and split on hyphens and slashes. + datestring = super(DateField, self).__get__(mediafile, None) + if isinstance(datestring, six.string_types): + datestring = re.sub(r'[Tt ].*$', '', six.text_type(datestring)) + items = re.split('[-/]', six.text_type(datestring)) + else: + items = [] + + # Ensure that we have exactly 3 components, possibly by + # truncating or padding. + items = items[:3] + if len(items) < 3: + items += [None] * (3 - len(items)) + + # Use year field if year is missing. + if not items[0] and hasattr(self, '_year_field'): + items[0] = self._year_field.__get__(mediafile) + + # Convert each component to an integer if possible. + items_ = [] + for item in items: + try: + items_.append(int(item)) + except (TypeError, ValueError): + items_.append(None) + return items_ + + def _set_date_tuple(self, mediafile, year, month=None, day=None): + """Set the value of the field given a year, month, and day + number. Each number can be an integer or None to indicate an + unset component. + """ + if year is None: + self.__delete__(mediafile) + return + + date = [u'{0:04d}'.format(int(year))] + if month: + date.append(u'{0:02d}'.format(int(month))) + if month and day: + date.append(u'{0:02d}'.format(int(day))) + date = map(six.text_type, date) + super(DateField, self).__set__(mediafile, u'-'.join(date)) + + if hasattr(self, '_year_field'): + self._year_field.__set__(mediafile, year) + + def year_field(self): + return DateItemField(self, 0) + + def month_field(self): + return DateItemField(self, 1) + + def day_field(self): + return DateItemField(self, 2) + + +class DateItemField(MediaField): + """Descriptor that gets and sets constituent parts of a `DateField`: + the month, day, or year. + """ + def __init__(self, date_field, item_pos): + self.date_field = date_field + self.item_pos = item_pos + + def __get__(self, mediafile, _): + return self.date_field._get_date_tuple(mediafile)[self.item_pos] + + def __set__(self, mediafile, value): + items = self.date_field._get_date_tuple(mediafile) + items[self.item_pos] = value + self.date_field._set_date_tuple(mediafile, *items) + + def __delete__(self, mediafile): + self.__set__(mediafile, None) + + +class CoverArtField(MediaField): + """A descriptor that provides access to the *raw image data* for the + cover image on a file. This is used for backwards compatibility: the + full `ImageListField` provides richer `Image` objects. + + When there are multiple images we try to pick the most likely to be a front + cover. + """ + def __init__(self): + pass + + def __get__(self, mediafile, _): + candidates = mediafile.images + if candidates: + return self.guess_cover_image(candidates).data + else: + return None + + @staticmethod + def guess_cover_image(candidates): + if len(candidates) == 1: + return candidates[0] + try: + return next(c for c in candidates if c.type == ImageType.front) + except StopIteration: + return candidates[0] + + def __set__(self, mediafile, data): + if data: + mediafile.images = [Image(data=data)] + else: + mediafile.images = [] + + def __delete__(self, mediafile): + delattr(mediafile, 'images') + + +class QNumberField(MediaField): + """Access integer-represented Q number fields. + + Access a fixed-point fraction as a float. The stored value is shifted by + `fraction_bits` binary digits to the left and then rounded, yielding a + simple integer. + """ + def __init__(self, fraction_bits, *args, **kwargs): + super(QNumberField, self).__init__(out_type=int, *args, **kwargs) + self.__fraction_bits = fraction_bits + + def __get__(self, mediafile, owner=None): + q_num = super(QNumberField, self).__get__(mediafile, owner) + if q_num is None: + return None + return q_num / pow(2, self.__fraction_bits) + + def __set__(self, mediafile, value): + q_num = round(value * pow(2, self.__fraction_bits)) + q_num = int(q_num) # needed for py2.7 + super(QNumberField, self).__set__(mediafile, q_num) + + +class ImageListField(ListMediaField): + """Descriptor to access the list of images embedded in tags. + + The getter returns a list of `Image` instances obtained from + the tags. The setter accepts a list of `Image` instances to be + written to the tags. + """ + def __init__(self): + # The storage styles used here must implement the + # `ListStorageStyle` interface and get and set lists of + # `Image`s. + super(ImageListField, self).__init__( + MP3ImageStorageStyle(), + MP4ImageStorageStyle(), + ASFImageStorageStyle(), + VorbisImageStorageStyle(), + FlacImageStorageStyle(), + APEv2ImageStorageStyle(), + out_type=Image, + ) + + +# MediaFile is a collection of fields. + +class MediaFile(object): + """Represents a multimedia file on disk and provides access to its + metadata. + """ + @loadfile() + def __init__(self, filething, id3v23=False): + """Constructs a new `MediaFile` reflecting the provided file. + + `filething` can be a path to a file (i.e., a string) or a + file-like object. + + May throw `UnreadableFileError`. + + By default, MP3 files are saved with ID3v2.4 tags. You can use + the older ID3v2.3 standard by specifying the `id3v23` option. + """ + self.filething = filething + + self.mgfile = mutagen_call( + 'open', self.filename, mutagen.File, filething + ) + + if self.mgfile is None: + # Mutagen couldn't guess the type + raise FileTypeError(self.filename) + elif type(self.mgfile).__name__ in ['M4A', 'MP4']: + info = self.mgfile.info + if info.codec and info.codec.startswith('alac'): + self.type = 'alac' + else: + self.type = 'aac' + elif type(self.mgfile).__name__ in ['ID3', 'MP3']: + self.type = 'mp3' + elif type(self.mgfile).__name__ == 'FLAC': + self.type = 'flac' + elif type(self.mgfile).__name__ == 'OggOpus': + self.type = 'opus' + elif type(self.mgfile).__name__ == 'OggVorbis': + self.type = 'ogg' + elif type(self.mgfile).__name__ == 'MonkeysAudio': + self.type = 'ape' + elif type(self.mgfile).__name__ == 'WavPack': + self.type = 'wv' + elif type(self.mgfile).__name__ == 'Musepack': + self.type = 'mpc' + elif type(self.mgfile).__name__ == 'ASF': + self.type = 'asf' + elif type(self.mgfile).__name__ == 'AIFF': + self.type = 'aiff' + elif type(self.mgfile).__name__ == 'DSF': + self.type = 'dsf' + elif type(self.mgfile).__name__ == 'WAVE': + self.type = 'wav' + else: + raise FileTypeError(self.filename, type(self.mgfile).__name__) + + # Add a set of tags if it's missing. + if self.mgfile.tags is None: + self.mgfile.add_tags() + + # Set the ID3v2.3 flag only for MP3s. + self.id3v23 = id3v23 and self.type == 'mp3' + + @property + def filename(self): + """The name of the file. + + This is the path if this object was opened from the filesystem, + or the name of the file-like object. + """ + return self.filething.name + + @filename.setter + def filename(self, val): + """Silently skips setting filename. + Workaround for `mutagen._util._openfile` setting instance's filename. + """ + pass + + @property + def path(self): + """The path to the file. + + This is `None` if the data comes from a file-like object instead + of a filesystem path. + """ + return self.filething.filename + + @property + def filesize(self): + """The size (in bytes) of the underlying file. + """ + if self.filething.filename: + return os.path.getsize(self.filething.filename) + if hasattr(self.filething.fileobj, '__len__'): + return len(self.filething.fileobj) + else: + tell = self.filething.fileobj.tell() + filesize = self.filething.fileobj.seek(0, 2) + self.filething.fileobj.seek(tell) + return filesize + + def save(self, **kwargs): + """Write the object's tags back to the file. + + May throw `UnreadableFileError`. Accepts keyword arguments to be + passed to Mutagen's `save` function. + """ + # Possibly save the tags to ID3v2.3. + if self.id3v23: + id3 = self.mgfile + if hasattr(id3, 'tags'): + # In case this is an MP3 object, not an ID3 object. + id3 = id3.tags + id3.update_to_v23() + kwargs['v2_version'] = 3 + + mutagen_call('save', self.filename, self.mgfile.save, + _update_filething(self.filething), **kwargs) + + def delete(self): + """Remove the current metadata tag from the file. May + throw `UnreadableFileError`. + """ + mutagen_call('delete', self.filename, self.mgfile.delete, + _update_filething(self.filething)) + + # Convenient access to the set of available fields. + + @classmethod + def fields(cls): + """Get the names of all writable properties that reflect + metadata tags (i.e., those that are instances of + :class:`MediaField`). + """ + for property, descriptor in cls.__dict__.items(): + if isinstance(descriptor, MediaField): + if isinstance(property, bytes): + # On Python 2, class field names are bytes. This method + # produces text strings. + yield property.decode('utf8', 'ignore') + else: + yield property + + @classmethod + def _field_sort_name(cls, name): + """Get a sort key for a field name that determines the order + fields should be written in. + + Fields names are kept unchanged, unless they are instances of + :class:`DateItemField`, in which case `year`, `month`, and `day` + are replaced by `date0`, `date1`, and `date2`, respectively, to + make them appear in that order. + """ + if isinstance(cls.__dict__[name], DateItemField): + name = re.sub('year', 'date0', name) + name = re.sub('month', 'date1', name) + name = re.sub('day', 'date2', name) + return name + + @classmethod + def sorted_fields(cls): + """Get the names of all writable metadata fields, sorted in the + order that they should be written. + + This is a lexicographic order, except for instances of + :class:`DateItemField`, which are sorted in year-month-day + order. + """ + for property in sorted(cls.fields(), key=cls._field_sort_name): + yield property + + @classmethod + def readable_fields(cls): + """Get all metadata fields: the writable ones from + :meth:`fields` and also other audio properties. + """ + for property in cls.fields(): + yield property + for property in ('length', 'samplerate', 'bitdepth', 'bitrate', + 'bitrate_mode', 'channels', 'encoder_info', + 'encoder_settings', 'format'): + yield property + + @classmethod + def add_field(cls, name, descriptor): + """Add a field to store custom tags. + + :param name: the name of the property the field is accessed + through. It must not already exist on this class. + + :param descriptor: an instance of :class:`MediaField`. + """ + if not isinstance(descriptor, MediaField): + raise ValueError( + u'{0} must be an instance of MediaField'.format(descriptor)) + if name in cls.__dict__: + raise ValueError( + u'property "{0}" already exists on MediaFile'.format(name)) + setattr(cls, name, descriptor) + + def update(self, dict): + """Set all field values from a dictionary. + + For any key in `dict` that is also a field to store tags the + method retrieves the corresponding value from `dict` and updates + the `MediaFile`. If a key has the value `None`, the + corresponding property is deleted from the `MediaFile`. + """ + for field in self.sorted_fields(): + if field in dict: + if dict[field] is None: + delattr(self, field) + else: + setattr(self, field, dict[field]) + + def as_dict(self): + """Get a dictionary with all writable properties that reflect + metadata tags (i.e., those that are instances of + :class:`MediaField`). + """ + return dict((x, getattr(self, x)) for x in self.fields()) + + # Field definitions. + + title = MediaField( + MP3StorageStyle('TIT2'), + MP4StorageStyle('\xa9nam'), + StorageStyle('TITLE'), + ASFStorageStyle('Title'), + ) + artist = MediaField( + MP3StorageStyle('TPE1'), + MP4StorageStyle('\xa9ART'), + StorageStyle('ARTIST'), + ASFStorageStyle('Author'), + ) + artists = ListMediaField( + MP3ListDescStorageStyle(desc=u'ARTISTS'), + MP4ListStorageStyle('----:com.apple.iTunes:ARTISTS'), + ListStorageStyle('ARTISTS'), + ASFStorageStyle('WM/ARTISTS'), + ) + album = MediaField( + MP3StorageStyle('TALB'), + MP4StorageStyle('\xa9alb'), + StorageStyle('ALBUM'), + ASFStorageStyle('WM/AlbumTitle'), + ) + genres = ListMediaField( + MP3ListStorageStyle('TCON'), + MP4ListStorageStyle('\xa9gen'), + ListStorageStyle('GENRE'), + ASFStorageStyle('WM/Genre'), + ) + genre = genres.single_field() + + lyricist = MediaField( + MP3StorageStyle('TEXT'), + MP4StorageStyle('----:com.apple.iTunes:LYRICIST'), + StorageStyle('LYRICIST'), + ASFStorageStyle('WM/Writer'), + ) + composer = MediaField( + MP3StorageStyle('TCOM'), + MP4StorageStyle('\xa9wrt'), + StorageStyle('COMPOSER'), + ASFStorageStyle('WM/Composer'), + ) + composer_sort = MediaField( + MP3StorageStyle('TSOC'), + MP4StorageStyle('soco'), + StorageStyle('COMPOSERSORT'), + ASFStorageStyle('WM/Composersortorder'), + ) + arranger = MediaField( + MP3PeopleStorageStyle('TIPL', involvement='arranger'), + MP4StorageStyle('----:com.apple.iTunes:Arranger'), + StorageStyle('ARRANGER'), + ASFStorageStyle('beets/Arranger'), + ) + + grouping = MediaField( + MP3StorageStyle('TIT1'), + MP4StorageStyle('\xa9grp'), + StorageStyle('GROUPING'), + ASFStorageStyle('WM/ContentGroupDescription'), + ) + track = MediaField( + MP3SlashPackStorageStyle('TRCK', pack_pos=0), + MP4TupleStorageStyle('trkn', index=0), + StorageStyle('TRACK'), + StorageStyle('TRACKNUMBER'), + ASFStorageStyle('WM/TrackNumber'), + out_type=int, + ) + tracktotal = MediaField( + MP3SlashPackStorageStyle('TRCK', pack_pos=1), + MP4TupleStorageStyle('trkn', index=1), + StorageStyle('TRACKTOTAL'), + StorageStyle('TRACKC'), + StorageStyle('TOTALTRACKS'), + ASFStorageStyle('TotalTracks'), + out_type=int, + ) + disc = MediaField( + MP3SlashPackStorageStyle('TPOS', pack_pos=0), + MP4TupleStorageStyle('disk', index=0), + StorageStyle('DISC'), + StorageStyle('DISCNUMBER'), + ASFStorageStyle('WM/PartOfSet'), + out_type=int, + ) + disctotal = MediaField( + MP3SlashPackStorageStyle('TPOS', pack_pos=1), + MP4TupleStorageStyle('disk', index=1), + StorageStyle('DISCTOTAL'), + StorageStyle('DISCC'), + StorageStyle('TOTALDISCS'), + ASFStorageStyle('TotalDiscs'), + out_type=int, + ) + + url = MediaField( + MP3DescStorageStyle(key='WXXX', attr='url', multispec=False), + MP4StorageStyle('\xa9url'), + StorageStyle('URL'), + ASFStorageStyle('WM/URL'), + ) + lyrics = MediaField( + MP3DescStorageStyle(key='USLT', multispec=False), + MP4StorageStyle('\xa9lyr'), + StorageStyle('LYRICS'), + ASFStorageStyle('WM/Lyrics'), + ) + comments = MediaField( + MP3DescStorageStyle(key='COMM'), + MP4StorageStyle('\xa9cmt'), + StorageStyle('DESCRIPTION'), + StorageStyle('COMMENT'), + ASFStorageStyle('WM/Comments'), + ASFStorageStyle('Description') + ) + copyright = MediaField( + MP3StorageStyle('TCOP'), + MP4StorageStyle('cprt'), + StorageStyle('COPYRIGHT'), + ASFStorageStyle('Copyright'), + ) + bpm = MediaField( + MP3StorageStyle('TBPM'), + MP4StorageStyle('tmpo', as_type=int), + StorageStyle('BPM'), + ASFStorageStyle('WM/BeatsPerMinute'), + out_type=int, + ) + comp = MediaField( + MP3StorageStyle('TCMP'), + MP4BoolStorageStyle('cpil'), + StorageStyle('COMPILATION'), + ASFStorageStyle('WM/IsCompilation', as_type=bool), + out_type=bool, + ) + albumartist = MediaField( + MP3StorageStyle('TPE2'), + MP4StorageStyle('aART'), + StorageStyle('ALBUM ARTIST'), + StorageStyle('ALBUM_ARTIST'), + StorageStyle('ALBUMARTIST'), + ASFStorageStyle('WM/AlbumArtist'), + ) + albumartists = ListMediaField( + MP3ListDescStorageStyle(desc=u'ALBUMARTISTS'), + MP3ListDescStorageStyle(desc=u'ALBUM_ARTISTS'), + MP3ListDescStorageStyle(desc=u'ALBUM ARTISTS', read_only=True), + MP4ListStorageStyle('----:com.apple.iTunes:ALBUMARTISTS'), + MP4ListStorageStyle('----:com.apple.iTunes:ALBUM_ARTISTS'), + MP4ListStorageStyle( + '----:com.apple.iTunes:ALBUM ARTISTS', read_only=True + ), + ListStorageStyle('ALBUMARTISTS'), + ListStorageStyle('ALBUM_ARTISTS'), + ListStorageStyle('ALBUM ARTISTS', read_only=True), + ASFStorageStyle('WM/AlbumArtists'), + ) + albumtypes = ListMediaField( + MP3ListDescStorageStyle('MusicBrainz Album Type', split_v23=True), + MP4ListStorageStyle('----:com.apple.iTunes:MusicBrainz Album Type'), + ListStorageStyle('RELEASETYPE'), + ListStorageStyle('MUSICBRAINZ_ALBUMTYPE'), + ASFStorageStyle('MusicBrainz/Album Type'), + ) + albumtype = albumtypes.single_field() + + label = MediaField( + MP3StorageStyle('TPUB'), + MP4StorageStyle('----:com.apple.iTunes:LABEL'), + MP4StorageStyle('----:com.apple.iTunes:publisher'), + MP4StorageStyle('----:com.apple.iTunes:Label', read_only=True), + StorageStyle('LABEL'), + StorageStyle('PUBLISHER'), # Traktor + ASFStorageStyle('WM/Publisher'), + ) + artist_sort = MediaField( + MP3StorageStyle('TSOP'), + MP4StorageStyle('soar'), + StorageStyle('ARTISTSORT'), + ASFStorageStyle('WM/ArtistSortOrder'), + ) + albumartist_sort = MediaField( + MP3DescStorageStyle(u'ALBUMARTISTSORT'), + MP4StorageStyle('soaa'), + StorageStyle('ALBUMARTISTSORT'), + ASFStorageStyle('WM/AlbumArtistSortOrder'), + ) + asin = MediaField( + MP3DescStorageStyle(u'ASIN'), + MP4StorageStyle('----:com.apple.iTunes:ASIN'), + StorageStyle('ASIN'), + ASFStorageStyle('MusicBrainz/ASIN'), + ) + catalognums = ListMediaField( + MP3ListDescStorageStyle('CATALOGNUMBER', split_v23=True), + MP3ListDescStorageStyle('CATALOGID', read_only=True), + MP3ListDescStorageStyle('DISCOGS_CATALOG', read_only=True), + MP4ListStorageStyle('----:com.apple.iTunes:CATALOGNUMBER'), + MP4ListStorageStyle( + '----:com.apple.iTunes:CATALOGID', read_only=True + ), + MP4ListStorageStyle( + '----:com.apple.iTunes:DISCOGS_CATALOG', read_only=True + ), + ListStorageStyle('CATALOGNUMBER'), + ListStorageStyle('CATALOGID', read_only=True), + ListStorageStyle('DISCOGS_CATALOG', read_only=True), + ASFStorageStyle('WM/CatalogNo'), + ASFStorageStyle('CATALOGID', read_only=True), + ASFStorageStyle('DISCOGS_CATALOG', read_only=True), + ) + catalognum = catalognums.single_field() + + barcode = MediaField( + MP3DescStorageStyle(u'BARCODE'), + MP4StorageStyle('----:com.apple.iTunes:BARCODE'), + StorageStyle('BARCODE'), + StorageStyle('UPC', read_only=True), + StorageStyle('EAN/UPN', read_only=True), + StorageStyle('EAN', read_only=True), + StorageStyle('UPN', read_only=True), + ASFStorageStyle('WM/Barcode'), + ) + isrc = MediaField( + MP3StorageStyle(u'TSRC'), + MP4StorageStyle('----:com.apple.iTunes:ISRC'), + StorageStyle('ISRC'), + ASFStorageStyle('WM/ISRC'), + ) + disctitle = MediaField( + MP3StorageStyle('TSST'), + MP4StorageStyle('----:com.apple.iTunes:DISCSUBTITLE'), + StorageStyle('DISCSUBTITLE'), + ASFStorageStyle('WM/SetSubTitle'), + ) + encoder = MediaField( + MP3StorageStyle('TENC'), + MP4StorageStyle('\xa9too'), + StorageStyle('ENCODEDBY'), + StorageStyle('ENCODER'), + ASFStorageStyle('WM/EncodedBy'), + ) + script = MediaField( + MP3DescStorageStyle(u'Script'), + MP4StorageStyle('----:com.apple.iTunes:SCRIPT'), + StorageStyle('SCRIPT'), + ASFStorageStyle('WM/Script'), + ) + languages = ListMediaField( + MP3ListStorageStyle('TLAN'), + MP4ListStorageStyle('----:com.apple.iTunes:LANGUAGE'), + ListStorageStyle('LANGUAGE'), + ASFStorageStyle('WM/Language'), + ) + language = languages.single_field() + + country = MediaField( + MP3DescStorageStyle(u'MusicBrainz Album Release Country'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz ' + 'Album Release Country'), + StorageStyle('RELEASECOUNTRY'), + ASFStorageStyle('MusicBrainz/Album Release Country'), + ) + albumstatus = MediaField( + MP3DescStorageStyle(u'MusicBrainz Album Status'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Status'), + StorageStyle('RELEASESTATUS'), + StorageStyle('MUSICBRAINZ_ALBUMSTATUS'), + ASFStorageStyle('MusicBrainz/Album Status'), + ) + media = MediaField( + MP3StorageStyle('TMED'), + MP4StorageStyle('----:com.apple.iTunes:MEDIA'), + StorageStyle('MEDIA'), + ASFStorageStyle('WM/Media'), + ) + albumdisambig = MediaField( + # This tag mapping was invented for beets (not used by Picard, etc). + MP3DescStorageStyle(u'MusicBrainz Album Comment'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Comment'), + StorageStyle('MUSICBRAINZ_ALBUMCOMMENT'), + ASFStorageStyle('MusicBrainz/Album Comment'), + ) + + # Release date. + date = DateField( + MP3StorageStyle('TDRC'), + MP4StorageStyle('\xa9day'), + StorageStyle('DATE'), + ASFStorageStyle('WM/Year'), + year=(StorageStyle('YEAR'),)) + + year = date.year_field() + month = date.month_field() + day = date.day_field() + + # *Original* release date. + original_date = DateField( + MP3StorageStyle('TDOR'), + MP4StorageStyle('----:com.apple.iTunes:ORIGINAL YEAR'), + StorageStyle('ORIGINALDATE'), + ASFStorageStyle('WM/OriginalReleaseYear')) + + original_year = original_date.year_field() + original_month = original_date.month_field() + original_day = original_date.day_field() + + # Nonstandard metadata. + artist_credit = MediaField( + MP3DescStorageStyle(u'Artist Credit'), + MP4StorageStyle('----:com.apple.iTunes:Artist Credit'), + StorageStyle('ARTIST_CREDIT'), + ASFStorageStyle('beets/Artist Credit'), + ) + albumartist_credit = MediaField( + MP3DescStorageStyle(u'Album Artist Credit'), + MP4StorageStyle('----:com.apple.iTunes:Album Artist Credit'), + StorageStyle('ALBUMARTIST_CREDIT'), + ASFStorageStyle('beets/Album Artist Credit'), + ) + + # Legacy album art field + art = CoverArtField() + + # Image list + images = ImageListField() + + # MusicBrainz IDs. + mb_trackid = MediaField( + MP3UFIDStorageStyle(owner='http://musicbrainz.org'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Track Id'), + StorageStyle('MUSICBRAINZ_TRACKID'), + ASFStorageStyle('MusicBrainz/Track Id'), + ) + mb_releasetrackid = MediaField( + MP3DescStorageStyle(u'MusicBrainz Release Track Id'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Track Id'), + StorageStyle('MUSICBRAINZ_RELEASETRACKID'), + ASFStorageStyle('MusicBrainz/Release Track Id'), + ) + mb_workid = MediaField( + MP3DescStorageStyle(u'MusicBrainz Work Id'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Work Id'), + StorageStyle('MUSICBRAINZ_WORKID'), + ASFStorageStyle('MusicBrainz/Work Id'), + ) + mb_albumid = MediaField( + MP3DescStorageStyle(u'MusicBrainz Album Id'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id'), + StorageStyle('MUSICBRAINZ_ALBUMID'), + ASFStorageStyle('MusicBrainz/Album Id'), + ) + mb_artistids = ListMediaField( + MP3ListDescStorageStyle(u'MusicBrainz Artist Id', split_v23=True), + MP4ListStorageStyle('----:com.apple.iTunes:MusicBrainz Artist Id'), + ListStorageStyle('MUSICBRAINZ_ARTISTID'), + ASFStorageStyle('MusicBrainz/Artist Id'), + ) + mb_artistid = mb_artistids.single_field() + + mb_albumartistids = ListMediaField( + MP3ListDescStorageStyle( + u'MusicBrainz Album Artist Id', + split_v23=True, + ), + MP4ListStorageStyle( + '----:com.apple.iTunes:MusicBrainz Album Artist Id', + ), + ListStorageStyle('MUSICBRAINZ_ALBUMARTISTID'), + ASFStorageStyle('MusicBrainz/Album Artist Id'), + ) + mb_albumartistid = mb_albumartistids.single_field() + + mb_releasegroupid = MediaField( + MP3DescStorageStyle(u'MusicBrainz Release Group Id'), + MP4StorageStyle('----:com.apple.iTunes:MusicBrainz Release Group Id'), + StorageStyle('MUSICBRAINZ_RELEASEGROUPID'), + ASFStorageStyle('MusicBrainz/Release Group Id'), + ) + + # Acoustid fields. + acoustid_fingerprint = MediaField( + MP3DescStorageStyle(u'Acoustid Fingerprint'), + MP4StorageStyle('----:com.apple.iTunes:Acoustid Fingerprint'), + StorageStyle('ACOUSTID_FINGERPRINT'), + ASFStorageStyle('Acoustid/Fingerprint'), + ) + acoustid_id = MediaField( + MP3DescStorageStyle(u'Acoustid Id'), + MP4StorageStyle('----:com.apple.iTunes:Acoustid Id'), + StorageStyle('ACOUSTID_ID'), + ASFStorageStyle('Acoustid/Id'), + ) + + # ReplayGain fields. + rg_track_gain = MediaField( + MP3DescStorageStyle( + u'REPLAYGAIN_TRACK_GAIN', + float_places=2, suffix=u' dB' + ), + MP3DescStorageStyle( + u'replaygain_track_gain', + float_places=2, suffix=u' dB' + ), + MP3SoundCheckStorageStyle( + key='COMM', + index=0, desc=u'iTunNORM', + id3_lang='eng' + ), + MP4StorageStyle( + '----:com.apple.iTunes:replaygain_track_gain', + float_places=2, suffix=' dB' + ), + MP4SoundCheckStorageStyle( + '----:com.apple.iTunes:iTunNORM', + index=0 + ), + StorageStyle( + u'REPLAYGAIN_TRACK_GAIN', + float_places=2, suffix=u' dB' + ), + ASFStorageStyle( + u'replaygain_track_gain', + float_places=2, suffix=u' dB' + ), + out_type=float + ) + rg_album_gain = MediaField( + MP3DescStorageStyle( + u'REPLAYGAIN_ALBUM_GAIN', + float_places=2, suffix=u' dB' + ), + MP3DescStorageStyle( + u'replaygain_album_gain', + float_places=2, suffix=u' dB' + ), + MP4StorageStyle( + '----:com.apple.iTunes:replaygain_album_gain', + float_places=2, suffix=' dB' + ), + StorageStyle( + u'REPLAYGAIN_ALBUM_GAIN', + float_places=2, suffix=u' dB' + ), + ASFStorageStyle( + u'replaygain_album_gain', + float_places=2, suffix=u' dB' + ), + out_type=float + ) + rg_track_peak = MediaField( + MP3DescStorageStyle( + u'REPLAYGAIN_TRACK_PEAK', + float_places=6 + ), + MP3DescStorageStyle( + u'replaygain_track_peak', + float_places=6 + ), + MP3SoundCheckStorageStyle( + key=u'COMM', + index=1, desc=u'iTunNORM', + id3_lang='eng' + ), + MP4StorageStyle( + '----:com.apple.iTunes:replaygain_track_peak', + float_places=6 + ), + MP4SoundCheckStorageStyle( + '----:com.apple.iTunes:iTunNORM', + index=1 + ), + StorageStyle(u'REPLAYGAIN_TRACK_PEAK', float_places=6), + ASFStorageStyle(u'replaygain_track_peak', float_places=6), + out_type=float, + ) + rg_album_peak = MediaField( + MP3DescStorageStyle( + u'REPLAYGAIN_ALBUM_PEAK', + float_places=6 + ), + MP3DescStorageStyle( + u'replaygain_album_peak', + float_places=6 + ), + MP4StorageStyle( + '----:com.apple.iTunes:replaygain_album_peak', + float_places=6 + ), + StorageStyle(u'REPLAYGAIN_ALBUM_PEAK', float_places=6), + ASFStorageStyle(u'replaygain_album_peak', float_places=6), + out_type=float, + ) + + # EBU R128 fields. + r128_track_gain = QNumberField( + 8, + MP3DescStorageStyle( + u'R128_TRACK_GAIN' + ), + MP4StorageStyle( + '----:com.apple.iTunes:R128_TRACK_GAIN' + ), + StorageStyle( + u'R128_TRACK_GAIN' + ), + ASFStorageStyle( + u'R128_TRACK_GAIN' + ), + ) + r128_album_gain = QNumberField( + 8, + MP3DescStorageStyle( + u'R128_ALBUM_GAIN' + ), + MP4StorageStyle( + '----:com.apple.iTunes:R128_ALBUM_GAIN' + ), + StorageStyle( + u'R128_ALBUM_GAIN' + ), + ASFStorageStyle( + u'R128_ALBUM_GAIN' + ), + ) + + initial_key = MediaField( + MP3StorageStyle('TKEY'), + MP4StorageStyle('----:com.apple.iTunes:initialkey'), + StorageStyle('INITIALKEY'), + ASFStorageStyle('INITIALKEY'), + ) + + @property + def length(self): + """The duration of the audio in seconds (a float).""" + return self.mgfile.info.length + + @property + def samplerate(self): + """The audio's sample rate (an int).""" + if hasattr(self.mgfile.info, 'sample_rate'): + return self.mgfile.info.sample_rate + elif self.type == 'opus': + # Opus is always 48kHz internally. + return 48000 + return 0 + + @property + def bitdepth(self): + """The number of bits per sample in the audio encoding (an int). + Only available for certain file formats (zero where + unavailable). + """ + if hasattr(self.mgfile.info, 'bits_per_sample'): + return self.mgfile.info.bits_per_sample + return 0 + + @property + def channels(self): + """The number of channels in the audio (an int).""" + if hasattr(self.mgfile.info, 'channels'): + return self.mgfile.info.channels + return 0 + + @property + def bitrate(self): + """The number of bits per seconds used in the audio coding (an + int). If this is provided explicitly by the compressed file + format, this is a precise reflection of the encoding. Otherwise, + it is estimated from the on-disk file size. In this case, some + imprecision is possible because the file header is incorporated + in the file size. + """ + if hasattr(self.mgfile.info, 'bitrate') and self.mgfile.info.bitrate: + # Many formats provide it explicitly. + return self.mgfile.info.bitrate + else: + # Otherwise, we calculate bitrate from the file size. (This + # is the case for all of the lossless formats.) + if not self.length: + # Avoid division by zero if length is not available. + return 0 + return int(self.filesize * 8 / self.length) + + @property + def bitrate_mode(self): + """The mode of the bitrate used in the audio coding + (a string, eg. "CBR", "VBR" or "ABR"). + Only available for the MP3 file format (empty where unavailable). + """ + if hasattr(self.mgfile.info, 'bitrate_mode'): + return { + mutagen.mp3.BitrateMode.CBR: 'CBR', + mutagen.mp3.BitrateMode.VBR: 'VBR', + mutagen.mp3.BitrateMode.ABR: 'ABR', + }.get(self.mgfile.info.bitrate_mode, '') + else: + return '' + + @property + def encoder_info(self): + """The name and/or version of the encoder used + (a string, eg. "LAME 3.97.0"). + Only available for some formats (empty where unavailable). + """ + if hasattr(self.mgfile.info, 'encoder_info'): + return self.mgfile.info.encoder_info + else: + return '' + + @property + def encoder_settings(self): + """A guess of the settings used for the encoder (a string, eg. "-V2"). + Only available for the MP3 file format (empty where unavailable). + """ + if hasattr(self.mgfile.info, 'encoder_settings'): + return self.mgfile.info.encoder_settings + else: + return '' + + @property + def format(self): + """A string describing the file format/codec.""" + return TYPES[self.type] diff --git a/libs/common/munkres.py b/libs/common/munkres.py index 3a70ff06..2f2edbc5 100644 --- a/libs/common/munkres.py +++ b/libs/common/munkres.py @@ -1,8 +1,3 @@ -#!/usr/bin/env python -# -*- coding: iso-8859-1 -*- - -# Documentation is intended to be processed by Epydoc. - """ Introduction ============ @@ -11,286 +6,10 @@ The Munkres module provides an implementation of the Munkres algorithm (also called the Hungarian algorithm or the Kuhn-Munkres algorithm), useful for solving the Assignment Problem. -Assignment Problem -================== - -Let *C* be an *n* by *n* matrix representing the costs of each of *n* workers -to perform any of *n* jobs. The assignment problem is to assign jobs to -workers in a way that minimizes the total cost. Since each worker can perform -only one job and each job can be assigned to only one worker the assignments -represent an independent set of the matrix *C*. - -One way to generate the optimal set is to create all permutations of -the indexes necessary to traverse the matrix so that no row and column -are used more than once. For instance, given this matrix (expressed in -Python): - - matrix = [[5, 9, 1], - [10, 3, 2], - [8, 7, 4]] - -You could use this code to generate the traversal indexes: - - def permute(a, results): - if len(a) == 1: - results.insert(len(results), a) - - else: - for i in range(0, len(a)): - element = a[i] - a_copy = [a[j] for j in range(0, len(a)) if j != i] - subresults = [] - permute(a_copy, subresults) - for subresult in subresults: - result = [element] + subresult - results.insert(len(results), result) - - results = [] - permute(range(len(matrix)), results) # [0, 1, 2] for a 3x3 matrix - -After the call to permute(), the results matrix would look like this: - - [[0, 1, 2], - [0, 2, 1], - [1, 0, 2], - [1, 2, 0], - [2, 0, 1], - [2, 1, 0]] - -You could then use that index matrix to loop over the original cost matrix -and calculate the smallest cost of the combinations: - - minval = sys.maxsize - for indexes in results: - cost = 0 - for row, col in enumerate(indexes): - cost += matrix[row][col] - minval = min(cost, minval) - - print minval - -While this approach works fine for small matrices, it does not scale. It -executes in O(*n*!) time: Calculating the permutations for an *n*\ x\ *n* -matrix requires *n*! operations. For a 12x12 matrix, that's 479,001,600 -traversals. Even if you could manage to perform each traversal in just one -millisecond, it would still take more than 133 hours to perform the entire -traversal. A 20x20 matrix would take 2,432,902,008,176,640,000 operations. At -an optimistic millisecond per operation, that's more than 77 million years. - -The Munkres algorithm runs in O(*n*\ ^3) time, rather than O(*n*!). This -package provides an implementation of that algorithm. - -This version is based on -http://csclab.murraystate.edu/~bob.pilgrim/445/munkres.html - -This version was written for Python by Brian Clapper from the algorithm -at the above web site. (The ``Algorithm:Munkres`` Perl version, in CPAN, was -clearly adapted from the same web site.) - -Usage -===== - -Construct a Munkres object: - - from munkres import Munkres - - m = Munkres() - -Then use it to compute the lowest cost assignment from a cost matrix. Here's -a sample program: - - from munkres import Munkres, print_matrix - - matrix = [[5, 9, 1], - [10, 3, 2], - [8, 7, 4]] - m = Munkres() - indexes = m.compute(matrix) - print_matrix(matrix, msg='Lowest cost through this matrix:') - total = 0 - for row, column in indexes: - value = matrix[row][column] - total += value - print '(%d, %d) -> %d' % (row, column, value) - print 'total cost: %d' % total - -Running that program produces: - - Lowest cost through this matrix: - [5, 9, 1] - [10, 3, 2] - [8, 7, 4] - (0, 0) -> 5 - (1, 1) -> 3 - (2, 2) -> 4 - total cost=12 - -The instantiated Munkres object can be used multiple times on different -matrices. - -Non-square Cost Matrices -======================== - -The Munkres algorithm assumes that the cost matrix is square. However, it's -possible to use a rectangular matrix if you first pad it with 0 values to make -it square. This module automatically pads rectangular cost matrices to make -them square. - -Notes: - -- The module operates on a *copy* of the caller's matrix, so any padding will - not be seen by the caller. -- The cost matrix must be rectangular or square. An irregular matrix will - *not* work. - -Calculating Profit, Rather than Cost -==================================== - -The cost matrix is just that: A cost matrix. The Munkres algorithm finds -the combination of elements (one from each row and column) that results in -the smallest cost. It's also possible to use the algorithm to maximize -profit. To do that, however, you have to convert your profit matrix to a -cost matrix. The simplest way to do that is to subtract all elements from a -large value. For example: - - from munkres import Munkres, print_matrix - - matrix = [[5, 9, 1], - [10, 3, 2], - [8, 7, 4]] - cost_matrix = [] - for row in matrix: - cost_row = [] - for col in row: - cost_row += [sys.maxsize - col] - cost_matrix += [cost_row] - - m = Munkres() - indexes = m.compute(cost_matrix) - print_matrix(matrix, msg='Highest profit through this matrix:') - total = 0 - for row, column in indexes: - value = matrix[row][column] - total += value - print '(%d, %d) -> %d' % (row, column, value) - - print 'total profit=%d' % total - -Running that program produces: - - Highest profit through this matrix: - [5, 9, 1] - [10, 3, 2] - [8, 7, 4] - (0, 1) -> 9 - (1, 0) -> 10 - (2, 2) -> 4 - total profit=23 - -The ``munkres`` module provides a convenience method for creating a cost -matrix from a profit matrix. By default, it calculates the maximum profit -and subtracts every profit from it to obtain a cost. If, however, you -need a more general function, you can provide the -conversion function; but the convenience method takes care of the actual -creation of the matrix: - - import munkres - - cost_matrix = munkres.make_cost_matrix( - matrix, - lambda profit: 1000.0 - math.sqrt(profit)) - -So, the above profit-calculation program can be recast as: - - from munkres import Munkres, print_matrix, make_cost_matrix - - matrix = [[5, 9, 1], - [10, 3, 2], - [8, 7, 4]] - cost_matrix = make_cost_matrix(matrix) - # cost_matrix == [[5, 1, 9], - # [0, 7, 8], - # [2, 3, 6]] - m = Munkres() - indexes = m.compute(cost_matrix) - print_matrix(matrix, msg='Highest profits through this matrix:') - total = 0 - for row, column in indexes: - value = matrix[row][column] - total += value - print '(%d, %d) -> %d' % (row, column, value) - print 'total profit=%d' % total - -Disallowed Assignments -====================== - -You can also mark assignments in your cost or profit matrix as disallowed. -Simply use the munkres.DISALLOWED constant. - - from munkres import Munkres, print_matrix, make_cost_matrix, DISALLOWED - - matrix = [[5, 9, DISALLOWED], - [10, DISALLOWED, 2], - [8, 7, 4]] - cost_matrix = make_cost_matrix(matrix, lambda cost: (sys.maxsize - cost) if - (cost != DISALLOWED) else DISALLOWED) - m = Munkres() - indexes = m.compute(cost_matrix) - print_matrix(matrix, msg='Highest profit through this matrix:') - total = 0 - for row, column in indexes: - value = matrix[row][column] - total += value - print '(%d, %d) -> %d' % (row, column, value) - print 'total profit=%d' % total - -Running this program produces: - - Lowest cost through this matrix: - [ 5, 9, D] - [10, D, 2] - [ 8, 7, 4] - (0, 1) -> 9 - (1, 0) -> 10 - (2, 2) -> 4 - total profit=23 - -References -========== - -1. http://www.public.iastate.edu/~ddoty/HungarianAlgorithm.html - -2. Harold W. Kuhn. The Hungarian Method for the assignment problem. - *Naval Research Logistics Quarterly*, 2:83-97, 1955. - -3. Harold W. Kuhn. Variants of the Hungarian method for assignment - problems. *Naval Research Logistics Quarterly*, 3: 253-258, 1956. - -4. Munkres, J. Algorithms for the Assignment and Transportation Problems. - *Journal of the Society of Industrial and Applied Mathematics*, - 5(1):32-38, March, 1957. - -5. http://en.wikipedia.org/wiki/Hungarian_algorithm - -Copyright and License -===================== - -Copyright 2008-2016 Brian M. Clapper - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +For complete usage documentation, see: https://software.clapper.org/munkres/ """ -__docformat__ = 'restructuredtext' +__docformat__ = 'markdown' # --------------------------------------------------------------------------- # Imports @@ -298,6 +17,7 @@ __docformat__ = 'restructuredtext' import sys import copy +from typing import Union, NewType, Sequence, Tuple, Optional, Callable # --------------------------------------------------------------------------- # Exports @@ -309,11 +29,14 @@ __all__ = ['Munkres', 'make_cost_matrix', 'DISALLOWED'] # Globals # --------------------------------------------------------------------------- +AnyNum = NewType('AnyNum', Union[int, float]) +Matrix = NewType('Matrix', Sequence[Sequence[AnyNum]]) + # Info about the module -__version__ = "1.0.12" +__version__ = "1.1.4" __author__ = "Brian Clapper, bmc@clapper.org" -__url__ = "http://software.clapper.org/munkres/" -__copyright__ = "(c) 2008-2017 Brian M. Clapper" +__url__ = "https://software.clapper.org/munkres/" +__copyright__ = "(c) 2008-2020 Brian M. Clapper" __license__ = "Apache Software License" # Constants @@ -353,30 +76,18 @@ class Munkres: self.marked = None self.path = None - def make_cost_matrix(profit_matrix, inversion_function): - """ - **DEPRECATED** - - Please use the module function ``make_cost_matrix()``. - """ - import munkres - return munkres.make_cost_matrix(profit_matrix, inversion_function) - - make_cost_matrix = staticmethod(make_cost_matrix) - - def pad_matrix(self, matrix, pad_value=0): + def pad_matrix(self, matrix: Matrix, pad_value: int=0) -> Matrix: """ Pad a possibly non-square matrix to make it square. - :Parameters: - matrix : list of lists - matrix to pad + **Parameters** - pad_value : int - value to use to pad the matrix + - `matrix` (list of lists of numbers): matrix to pad + - `pad_value` (`int`): value to use to pad the matrix - :rtype: list of lists - :return: a new, possibly padded, matrix + **Returns** + + a new, possibly padded, matrix """ max_columns = 0 total_rows = len(matrix) @@ -400,26 +111,27 @@ class Munkres: return new_matrix - def compute(self, cost_matrix): + def compute(self, cost_matrix: Matrix) -> Sequence[Tuple[int, int]]: """ Compute the indexes for the lowest-cost pairings between rows and - columns in the database. Returns a list of (row, column) tuples + columns in the database. Returns a list of `(row, column)` tuples that can be used to traverse the matrix. - :Parameters: - cost_matrix : list of lists - The cost matrix. If this cost matrix is not square, it - will be padded with zeros, via a call to ``pad_matrix()``. - (This method does *not* modify the caller's matrix. It - operates on a copy of the matrix.) + **WARNING**: This code handles square and rectangular matrices. It + does *not* handle irregular matrices. - **WARNING**: This code handles square and rectangular - matrices. It does *not* handle irregular matrices. + **Parameters** - :rtype: list - :return: A list of ``(row, column)`` tuples that describe the lowest - cost path through the matrix + - `cost_matrix` (list of lists of numbers): The cost matrix. If this + cost matrix is not square, it will be padded with zeros, via a call + to `pad_matrix()`. (This method does *not* modify the caller's + matrix. It operates on a copy of the matrix.) + + **Returns** + + A list of `(row, column)` tuples that describe the lowest cost path + through the matrix """ self.C = self.pad_matrix(cost_matrix) self.n = len(self.C) @@ -458,18 +170,18 @@ class Munkres: return results - def __copy_matrix(self, matrix): + def __copy_matrix(self, matrix: Matrix) -> Matrix: """Return an exact copy of the supplied matrix""" return copy.deepcopy(matrix) - def __make_matrix(self, n, val): + def __make_matrix(self, n: int, val: AnyNum) -> Matrix: """Create an *n*x*n* matrix, populating it with the specific value.""" matrix = [] for i in range(n): matrix += [[val for j in range(n)]] return matrix - def __step1(self): + def __step1(self) -> int: """ For each row of the matrix, find the smallest element and subtract it from every element in its row. Go to Step 2. @@ -492,7 +204,7 @@ class Munkres: self.C[i][j] -= minval return 2 - def __step2(self): + def __step2(self) -> int: """ Find a zero (Z) in the resulting matrix. If there is no starred zero in its row or column, star Z. Repeat for each element in the @@ -512,7 +224,7 @@ class Munkres: self.__clear_covers() return 3 - def __step3(self): + def __step3(self) -> int: """ Cover each column containing a starred zero. If K columns are covered, the starred zeros describe a complete set of unique @@ -533,7 +245,7 @@ class Munkres: return step - def __step4(self): + def __step4(self) -> int: """ Find a noncovered zero and prime it. If there is no starred zero in the row containing this primed zero, Go to Step 5. Otherwise, @@ -566,7 +278,7 @@ class Munkres: return step - def __step5(self): + def __step5(self) -> int: """ Construct a series of alternating primed and starred zeros as follows. Let Z0 represent the uncovered primed zero found in Step 4. @@ -602,7 +314,7 @@ class Munkres: self.__erase_primes() return 3 - def __step6(self): + def __step6(self) -> int: """ Add the value found in Step 4 to every element of each covered row, and subtract it from every element of each uncovered column. @@ -627,7 +339,7 @@ class Munkres: raise UnsolvableMatrix("Matrix cannot be solved!") return 4 - def __find_smallest(self): + def __find_smallest(self) -> AnyNum: """Find the smallest uncovered value in the matrix.""" minval = sys.maxsize for i in range(self.n): @@ -638,7 +350,7 @@ class Munkres: return minval - def __find_a_zero(self, i0=0, j0=0): + def __find_a_zero(self, i0: int = 0, j0: int = 0) -> Tuple[int, int]: """Find the first uncovered element with value 0""" row = -1 col = -1 @@ -664,7 +376,7 @@ class Munkres: return (row, col) - def __find_star_in_row(self, row): + def __find_star_in_row(self, row: Sequence[AnyNum]) -> int: """ Find the first starred element in the specified row. Returns the column index, or -1 if no starred element was found. @@ -677,7 +389,7 @@ class Munkres: return col - def __find_star_in_col(self, col): + def __find_star_in_col(self, col: Sequence[AnyNum]) -> int: """ Find the first starred element in the specified row. Returns the row index, or -1 if no starred element was found. @@ -690,7 +402,7 @@ class Munkres: return row - def __find_prime_in_row(self, row): + def __find_prime_in_row(self, row) -> int: """ Find the first prime element in the specified row. Returns the column index, or -1 if no starred element was found. @@ -703,20 +415,22 @@ class Munkres: return col - def __convert_path(self, path, count): + def __convert_path(self, + path: Sequence[Sequence[int]], + count: int) -> None: for i in range(count+1): if self.marked[path[i][0]][path[i][1]] == 1: self.marked[path[i][0]][path[i][1]] = 0 else: self.marked[path[i][0]][path[i][1]] = 1 - def __clear_covers(self): + def __clear_covers(self) -> None: """Clear all covered matrix cells""" for i in range(self.n): self.row_covered[i] = False self.col_covered[i] = False - def __erase_primes(self): + def __erase_primes(self) -> None: """Erase all prime markings""" for i in range(self.n): for j in range(self.n): @@ -727,37 +441,38 @@ class Munkres: # Functions # --------------------------------------------------------------------------- -def make_cost_matrix(profit_matrix, inversion_function=None): +def make_cost_matrix( + profit_matrix: Matrix, + inversion_function: Optional[Callable[[AnyNum], AnyNum]] = None + ) -> Matrix: """ - Create a cost matrix from a profit matrix by calling - 'inversion_function' to invert each value. The inversion - function must take one numeric argument (of any type) and return - another numeric argument which is presumed to be the cost inverse - of the original profit. In case the inversion function is not provided, - calculate it as max(matrix) - matrix. + Create a cost matrix from a profit matrix by calling `inversion_function()` + to invert each value. The inversion function must take one numeric argument + (of any type) and return another numeric argument which is presumed to be + the cost inverse of the original profit value. If the inversion function + is not provided, a given cell's inverted value is calculated as + `max(matrix) - value`. This is a static method. Call it like this: - .. python: - + from munkres import Munkres cost_matrix = Munkres.make_cost_matrix(matrix, inversion_func) For example: - .. python: - + from munkres import Munkres cost_matrix = Munkres.make_cost_matrix(matrix, lambda x : sys.maxsize - x) - :Parameters: - profit_matrix : list of lists - The matrix to convert from a profit to a cost matrix + **Parameters** - inversion_function : function - The function to use to invert each entry in the profit matrix. - In case it is not provided, calculate it as max(matrix) - matrix. + - `profit_matrix` (list of lists of numbers): The matrix to convert from + profit to cost values. + - `inversion_function` (`function`): The function to use to invert each + entry in the profit matrix. - :rtype: list of lists - :return: The converted matrix + **Returns** + + A new matrix representing the inversion of `profix_matrix`. """ if not inversion_function: maximum = max(max(row) for row in profit_matrix) @@ -768,16 +483,14 @@ def make_cost_matrix(profit_matrix, inversion_function=None): cost_matrix.append([inversion_function(value) for value in row]) return cost_matrix -def print_matrix(matrix, msg=None): +def print_matrix(matrix: Matrix, msg: Optional[str] = None) -> None: """ - Convenience function: Displays the contents of a matrix of integers. + Convenience function: Displays the contents of a matrix. - :Parameters: - matrix : list of lists - Matrix to print + **Parameters** - msg : str - Optional message to print before displaying the matrix + - `matrix` (list of lists of numbers): The matrix to print + - `msg` (`str`): Optional message to print before displaying the matrix """ import math @@ -800,8 +513,8 @@ def print_matrix(matrix, msg=None): sep = '[' for val in row: if val is DISALLOWED: - formatted = ((format + 's') % DISALLOWED_PRINTVAL) - else: formatted = ((format + 'd') % val) + val = DISALLOWED_PRINTVAL + formatted = ((format + 's') % val) sys.stdout.write(sep + formatted) sep = ', ' sys.stdout.write(']\n') @@ -832,12 +545,24 @@ if __name__ == '__main__': [9, 7, 4]], 18), + # Square variant with floating point value + ([[10.1, 10.2, 8.3], + [9.4, 8.5, 1.6], + [9.7, 7.8, 4.9]], + 19.5), + # Rectangular variant ([[10, 10, 8, 11], [9, 8, 1, 1], [9, 7, 4, 10]], 15), + # Rectangular variant with floating point value + ([[10.01, 10.02, 8.03, 11.04], + [9.05, 8.06, 1.07, 1.08], + [9.09, 7.1, 4.11, 10.12]], + 15.2), + # Rectangular with DISALLOWED ([[4, 5, 6, DISALLOWED], [1, 9, 12, 11], @@ -845,12 +570,26 @@ if __name__ == '__main__': [12, 12, 12, 10]], 20), + # Rectangular variant with DISALLOWED and floating point value + ([[4.001, 5.002, 6.003, DISALLOWED], + [1.004, 9.005, 12.006, 11.007], + [DISALLOWED, 5.008, 4.009, DISALLOWED], + [12.01, 12.011, 12.012, 10.013]], + 20.028), + # DISALLOWED to force pairings ([[1, DISALLOWED, DISALLOWED, DISALLOWED], [DISALLOWED, 2, DISALLOWED, DISALLOWED], [DISALLOWED, DISALLOWED, 3, DISALLOWED], [DISALLOWED, DISALLOWED, DISALLOWED, 4]], - 10)] + 10), + + # DISALLOWED to force pairings with floating point value + ([[1.1, DISALLOWED, DISALLOWED, DISALLOWED], + [DISALLOWED, 2.2, DISALLOWED, DISALLOWED], + [DISALLOWED, DISALLOWED, 3.3, DISALLOWED], + [DISALLOWED, DISALLOWED, DISALLOWED, 4.4]], + 11.0)] m = Munkres() for cost_matrix, expected_total in matrices: @@ -860,6 +599,6 @@ if __name__ == '__main__': for r, c in indexes: x = cost_matrix[r][c] total_cost += x - print('(%d, %d) -> %d' % (r, c, x)) - print('lowest cost=%d' % total_cost) + print(('(%d, %d) -> %s' % (r, c, x))) + print(('lowest cost=%s' % total_cost)) assert expected_total == total_cost diff --git a/libs/common/musicbrainzngs/caa.py b/libs/common/musicbrainzngs/caa.py index 12fa8d35..caa0c5f3 100644 --- a/libs/common/musicbrainzngs/caa.py +++ b/libs/common/musicbrainzngs/caa.py @@ -13,15 +13,24 @@ import json from musicbrainzngs import compat from musicbrainzngs import musicbrainz +from musicbrainzngs.util import _unicode hostname = "coverartarchive.org" +https = True -def set_caa_hostname(new_hostname): +def set_caa_hostname(new_hostname, use_https=False): """Set the base hostname for Cover Art Archive requests. - Defaults to 'coverartarchive.org'.""" + Defaults to 'coverartarchive.org', accessing over https. + For backwards compatibility, `use_https` is False by default. + + :param str new_hostname: The hostname (and port) of the CAA server to connect to + :param bool use_https: `True` if the host should be accessed using https. Default is `False` +""" global hostname + global https hostname = new_hostname + https = use_https def _caa_request(mbid, imageid=None, size=None, entitytype="release"): @@ -31,7 +40,7 @@ def _caa_request(mbid, imageid=None, size=None, entitytype="release"): with :meth:`get_image_list`. :type imageid: str - :param size: 250, 500 + :param size: "250", "500", "1200" :type size: str or None :param entitytype: ``release`` or ``release-group`` @@ -45,7 +54,7 @@ def _caa_request(mbid, imageid=None, size=None, entitytype="release"): elif imageid: path.append(imageid) url = compat.urlunparse(( - 'http', + 'https' if https else 'http', hostname, '/%s' % '/'.join(path), '', @@ -78,7 +87,8 @@ def _caa_request(mbid, imageid=None, size=None, entitytype="release"): return resp else: # Otherwise it's json - return json.loads(resp) + data = _unicode(resp) + return json.loads(data) def get_image_list(releaseid): @@ -121,7 +131,7 @@ def get_release_group_image_front(releasegroupid, size=None): :meth:`get_image`. """ return get_image(releasegroupid, "front", size=size, - entitytype="release-group") + entitytype="release-group") def get_image_front(releaseid, size=None): @@ -158,10 +168,10 @@ def get_image(mbid, coverid, size=None, entitytype="release"): :meth:`get_image_list` :type coverid: int or str - :param size: 250, 500 or None. If it is None, the largest available picture - will be downloaded. If the image originally uploaded to the - Cover Art Archive was smaller than the requested size, only - the original image will be returned. + :param size: "250", "500", "1200" or None. If it is None, the largest + available picture will be downloaded. If the image originally + uploaded to the Cover Art Archive was smaller than the + requested size, only the original image will be returned. :type size: str or None :param entitytype: The type of entity for which to download the cover art. diff --git a/libs/common/musicbrainzngs/compat.py b/libs/common/musicbrainzngs/compat.py index 36574b5c..689e0834 100644 --- a/libs/common/musicbrainzngs/compat.py +++ b/libs/common/musicbrainzngs/compat.py @@ -40,11 +40,10 @@ is_py3 = (_ver[0] == 3) if is_py2: from StringIO import StringIO from urllib2 import HTTPPasswordMgr, HTTPDigestAuthHandler, Request,\ - HTTPHandler, build_opener, HTTPError, URLError,\ - build_opener + HTTPHandler, build_opener, HTTPError, URLError from httplib import BadStatusLine, HTTPException from urlparse import urlunparse - from urllib import urlencode + from urllib import urlencode, quote_plus bytes = str unicode = unicode @@ -55,7 +54,7 @@ elif is_py3: HTTPHandler, build_opener from urllib.error import HTTPError, URLError from http.client import HTTPException, BadStatusLine - from urllib.parse import urlunparse, urlencode + from urllib.parse import urlunparse, urlencode, quote_plus unicode = str bytes = bytes diff --git a/libs/common/musicbrainzngs/mbxml.py b/libs/common/musicbrainzngs/mbxml.py index 60236dc7..e85fe7cb 100644 --- a/libs/common/musicbrainzngs/mbxml.py +++ b/libs/common/musicbrainzngs/mbxml.py @@ -7,29 +7,26 @@ import re import xml.etree.ElementTree as ET import logging -from musicbrainzngs import util +from . import util -try: - from ET import fixtag -except: - # Python < 2.7 - def fixtag(tag, namespaces): - # given a decorated tag (of the form {uri}tag), return prefixed - # tag and namespace declaration, if any - if isinstance(tag, ET.QName): - tag = tag.text - namespace_uri, tag = tag[1:].split("}", 1) - prefix = namespaces.get(namespace_uri) - if prefix is None: - prefix = "ns%d" % len(namespaces) - namespaces[namespace_uri] = prefix - if prefix == "xml": - xmlns = None - else: - xmlns = ("xmlns:%s" % prefix, namespace_uri) - else: + +def fixtag(tag, namespaces): + # given a decorated tag (of the form {uri}tag), return prefixed + # tag and namespace declaration, if any + if isinstance(tag, ET.QName): + tag = tag.text + namespace_uri, tag = tag[1:].split("}", 1) + prefix = namespaces.get(namespace_uri) + if prefix is None: + prefix = "ns%d" % len(namespaces) + namespaces[namespace_uri] = prefix + if prefix == "xml": xmlns = None - return "%s:%s" % (prefix, tag), xmlns + else: + xmlns = ("xmlns:%s" % prefix, namespace_uri) + else: + xmlns = None + return "%s:%s" % (prefix, tag), xmlns NS_MAP = {"http://musicbrainz.org/ns/mmd-2.0#": "ws2", @@ -377,7 +374,7 @@ def parse_relation(relation): result.update(parse_elements(elements, inner_els, relation)) # We parse attribute-list again to get attributes that have both # text and attribute values - result.update(parse_elements([], {"attribute-list": parse_relation_attribute_list}, relation)) + result.update(parse_elements(['target-credit'], {"attribute-list": parse_relation_attribute_list}, relation)) return result @@ -505,7 +502,6 @@ def parse_recording(recording): "user-tag-list": parse_tag_list, "rating": parse_rating, "isrc-list": parse_external_id_list, - "echoprint-list": parse_external_id_list, "relation-list": parse_relation_list, "annotation": parse_annotation} diff --git a/libs/common/musicbrainzngs/musicbrainz.py b/libs/common/musicbrainzngs/musicbrainz.py index 953c79b8..beb0a79b 100644 --- a/libs/common/musicbrainzngs/musicbrainz.py +++ b/libs/common/musicbrainzngs/musicbrainz.py @@ -20,7 +20,7 @@ from musicbrainzngs import mbxml from musicbrainzngs import util from musicbrainzngs import compat -_version = "0.6" +_version = "0.7.1" _log = logging.getLogger("musicbrainzngs") LUCENE_SPECIAL = r'([+\-&|!(){}\[\]\^"~*?:\\\/])' @@ -54,11 +54,11 @@ VALID_INCLUDES = { 'recording': [ "artists", "releases", # Subqueries "discids", "media", "artist-credits", "isrcs", - "annotation", "aliases" + "work-level-rels", "annotation", "aliases" ] + TAG_INCLUDES + RATING_INCLUDES + RELATION_INCLUDES, 'release': [ "artists", "labels", "recordings", "release-groups", "media", - "artist-credits", "discids", "puids", "isrcs", + "artist-credits", "discids", "isrcs", "recording-level-rels", "work-level-rels", "annotation", "aliases" ] + TAG_INCLUDES + RELATION_INCLUDES, 'release-group': [ @@ -69,16 +69,15 @@ VALID_INCLUDES = { "annotation", "aliases" ] + RELATION_INCLUDES, 'work': [ - "artists", # Subqueries "aliases", "annotation" ] + TAG_INCLUDES + RATING_INCLUDES + RELATION_INCLUDES, 'url': RELATION_INCLUDES, 'discid': [ # Discid should be the same as release "artists", "labels", "recordings", "release-groups", "media", - "artist-credits", "discids", "puids", "isrcs", + "artist-credits", "discids", "isrcs", "recording-level-rels", "work-level-rels", "annotation", "aliases" ] + RELATION_INCLUDES, - 'isrc': ["artists", "releases", "puids", "isrcs"], + 'isrc': ["artists", "releases", "isrcs"], 'iswc': ["artists"], 'collection': ['releases'], } @@ -109,48 +108,58 @@ VALID_SEARCH_FIELDS = { 'entity', 'name', 'text', 'type' ], 'area': [ - 'aid', 'area', 'alias', 'begin', 'comment', 'end', 'ended', - 'iso', 'iso1', 'iso2', 'iso3', 'type' + 'aid', 'alias', 'area', 'areaaccent', 'begin', 'comment', 'end', + 'ended', 'iso', 'iso1', 'iso2', 'iso3', 'sortname', 'tag', 'type' ], 'artist': [ - 'arid', 'artist', 'artistaccent', 'alias', 'begin', 'comment', - 'country', 'end', 'ended', 'gender', 'ipi', 'sortname', 'tag', 'type', - 'area', 'beginarea', 'endarea' + 'alias', 'area', 'arid', 'artist', 'artistaccent', 'begin', 'beginarea', + 'comment', 'country', 'end', 'endarea', 'ended', 'gender', + 'ipi', 'isni', 'primary_alias', 'sortname', 'tag', 'type' + ], + 'event': [ + 'aid', 'alias', 'area', 'arid', 'artist', 'begin', 'comment', 'eid', + 'end', 'ended', 'event', 'eventaccent', 'pid', 'place', 'tag', 'type' + ], + 'instrument': [ + 'alias', 'comment', 'description', 'iid', 'instrument', + 'instrumentaccent', 'tag', 'type' ], 'label': [ - 'alias', 'begin', 'code', 'comment', 'country', 'end', 'ended', - 'ipi', 'label', 'labelaccent', 'laid', 'sortname', 'type', 'tag', - 'area' - ], - 'recording': [ - 'arid', 'artist', 'artistname', 'creditname', 'comment', - 'country', 'date', 'dur', 'format', 'isrc', 'number', - 'position', 'primarytype', 'puid', 'qdur', 'recording', - 'recordingaccent', 'reid', 'release', 'rgid', 'rid', - 'secondarytype', 'status', 'tnum', 'tracks', 'tracksrelease', - 'tag', 'type', 'video' - ], - 'release-group': [ - 'arid', 'artist', 'artistname', 'comment', 'creditname', - 'primarytype', 'rgid', 'releasegroup', 'releasegroupaccent', - 'releases', 'release', 'reid', 'secondarytype', 'status', + 'alias', 'area', 'begin', 'code', 'comment', 'country', 'end', 'ended', + 'ipi', 'label', 'labelaccent', 'laid', 'release_count', 'sortname', 'tag', 'type' ], + 'place': [ + 'address', 'alias', 'area', 'begin', 'comment', 'end', 'ended', 'lat', 'long', + 'pid', 'place', 'placeaccent', 'type' + ], + 'recording': [ + 'alias', 'arid', 'artist', 'artistname', 'comment', 'country', + 'creditname', 'date', 'dur', 'format', 'isrc', 'number', 'position', + 'primarytype', 'qdur', 'recording', 'recordingaccent', 'reid', + 'release', 'rgid', 'rid', 'secondarytype', 'status', 'tag', 'tid', + 'tnum', 'tracks', 'tracksrelease', 'type', 'video'], + + 'release-group': [ + 'alias', 'arid', 'artist', 'artistname', 'comment', 'creditname', + 'primarytype', 'reid', 'release', 'releasegroup', 'releasegroupaccent', + 'releases', 'rgid', 'secondarytype', 'status', 'tag', 'type' + ], 'release': [ - 'arid', 'artist', 'artistname', 'asin', 'barcode', 'creditname', - 'catno', 'comment', 'country', 'creditname', 'date', 'discids', - 'discidsmedium', 'format', 'laid', 'label', 'lang', 'mediums', - 'primarytype', 'puid', 'quality', 'reid', 'release', 'releaseaccent', - 'rgid', 'script', 'secondarytype', 'status', 'tag', 'tracks', - 'tracksmedium', 'type' + 'alias', 'arid', 'artist', 'artistname', 'asin', 'barcode', 'catno', + 'comment', 'country', 'creditname', 'date', 'discids', 'discidsmedium', + 'format', 'label', 'laid', 'lang', 'mediums', 'primarytype', 'quality', + 'reid', 'release', 'releaseaccent', 'rgid', 'script', 'secondarytype', + 'status', 'tag', 'tracks', 'tracksmedium', 'type' ], 'series': [ - 'alias', 'comment', 'sid', 'series', 'type' + 'alias', 'comment', 'orderingattribute', 'series', 'seriesaccent', + 'sid', 'tag', 'type' ], 'work': [ - 'alias', 'arid', 'artist', 'comment', 'iswc', 'lang', 'tag', - 'type', 'wid', 'work', 'workaccent' - ], + 'alias', 'arid', 'artist', 'comment', 'iswc', 'lang', 'recording', + 'recording_count', 'rid', 'tag', 'type', 'wid', 'work', 'workaccent' + ] } # Constants @@ -278,8 +287,6 @@ def _docstring_search(entity): def _docstring_impl(name, values): def _decorator(func): - # puids are allowed so nothing breaks, but not documented - if "puids" in values: values.remove("puids") vstr = ", ".join(values) args = {name: vstr} if func.__doc__: @@ -293,6 +300,7 @@ def _docstring_impl(name, values): user = password = "" hostname = "musicbrainz.org" +https = True _client = "" _useragent = "" @@ -317,12 +325,21 @@ def set_useragent(app, version, contact=None): _client = "%s-%s" % (app, version) _log.debug("set user-agent to %s" % _useragent) -def set_hostname(new_hostname): + +def set_hostname(new_hostname, use_https=False): """Set the hostname for MusicBrainz webservice requests. - Defaults to 'musicbrainz.org'. - You can also include a port: 'localhost:8000'.""" + Defaults to 'musicbrainz.org', accessing over https. + For backwards compatibility, `use_https` is False by default. + + :param str new_hostname: The hostname (and port) of the MusicBrainz server to connect to + :param bool use_https: `True` if the host should be accessed using https. Default is `False` + + Specify a non-standard port by adding it to the hostname, + for example 'localhost:8000'.""" global hostname + global https hostname = new_hostname + https = use_https # Rate limiting. @@ -626,7 +643,7 @@ def _mb_request(path, method='GET', auth_required=AUTH_NO, # Construct the full URL for the request, including hostname and # query string. url = compat.urlunparse(( - 'http', + 'https' if https else 'http', hostname, '/ws/2/%s' % path, '', @@ -737,10 +754,6 @@ def _do_mb_search(entity, query='', fields={}, raise InvalidSearchFieldError( '%s is not a valid search field for %s' % (key, entity) ) - elif key == "puid": - warn("PUID support was removed from server\n" - "the 'puid' field is ignored", - Warning, stacklevel=2) # Escape Lucene's special characters. value = util._unicode(value) @@ -1024,27 +1037,6 @@ def get_releases_by_discid(id, includes=[], toc=None, cdstubs=True, media_format params["media-format"] = media_format return _do_mb_query("discid", id, includes, params) -@_docstring_get("recording") -def get_recordings_by_echoprint(echoprint, includes=[], release_status=[], - release_type=[]): - """Search for recordings with an `echoprint `_. - (not available on server)""" - warn("Echoprints were never introduced\n" - "and will not be found (404)", - Warning, stacklevel=2) - raise ResponseError(cause=compat.HTTPError( - None, 404, "Not Found", None, None)) - -@_docstring_get("recording") -def get_recordings_by_puid(puid, includes=[], release_status=[], - release_type=[]): - """Search for recordings with a :musicbrainz:`PUID`. - (not available on server)""" - warn("PUID support was removed from the server\n" - "and no PUIDs will be found (404)", - Warning, stacklevel=2) - raise ResponseError(cause=compat.HTTPError( - None, 404, "Not Found", None, None)) @_docstring_get("recording") def get_recordings_by_isrc(isrc, includes=[], release_status=[], @@ -1261,23 +1253,6 @@ def submit_barcodes(release_barcode): query = mbxml.make_barcode_request(release_barcode) return _do_mb_post("release", query) -def submit_puids(recording_puids): - """Submit PUIDs. - (Functionality removed from server) - """ - warn("PUID support was dropped at the server\n" - "nothing will be submitted", - Warning, stacklevel=2) - return {'message': {'text': 'OK'}} - -def submit_echoprints(recording_echoprints): - """Submit echoprints. - (Functionality removed from server) - """ - warn("Echoprints were never introduced\n" - "nothing will be submitted", - Warning, stacklevel=2) - return {'message': {'text': 'OK'}} def submit_isrcs(recording_isrcs): """Submit ISRCs. diff --git a/libs/common/mutagen/__init__.py b/libs/common/mutagen/__init__.py index 94f2509c..efff8dee 100644 --- a/libs/common/mutagen/__init__.py +++ b/libs/common/mutagen/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -23,7 +22,7 @@ from mutagen._util import MutagenError from mutagen._file import FileType, StreamInfo, File from mutagen._tags import Tags, Metadata, PaddingInfo -version = (1, 41, 1) +version = (1, 46, 0) """Version tuple.""" version_string = ".".join(map(str, version)) diff --git a/libs/common/mutagen/_compat.py b/libs/common/mutagen/_compat.py deleted file mode 100644 index 8a60d68d..00000000 --- a/libs/common/mutagen/_compat.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2013 Christoph Reiter -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -import sys - - -PY2 = sys.version_info[0] == 2 -PY3 = not PY2 - -if PY2: - from StringIO import StringIO - BytesIO = StringIO - from cStringIO import StringIO as cBytesIO - from itertools import izip - - long_ = long - integer_types = (int, long) - string_types = (str, unicode) - text_type = unicode - - xrange = xrange - cmp = cmp - chr_ = chr - - def endswith(text, end): - return text.endswith(end) - - iteritems = lambda d: d.iteritems() - itervalues = lambda d: d.itervalues() - iterkeys = lambda d: d.iterkeys() - - iterbytes = lambda b: iter(b) - - exec("def reraise(tp, value, tb):\n raise tp, value, tb") - - def swap_to_string(cls): - if "__str__" in cls.__dict__: - cls.__unicode__ = cls.__str__ - - if "__bytes__" in cls.__dict__: - cls.__str__ = cls.__bytes__ - - return cls - - import __builtin__ as builtins - builtins - -elif PY3: - from io import StringIO - StringIO = StringIO - from io import BytesIO - cBytesIO = BytesIO - - long_ = int - integer_types = (int,) - string_types = (str,) - text_type = str - - izip = zip - xrange = range - cmp = lambda a, b: (a > b) - (a < b) - chr_ = lambda x: bytes([x]) - - def endswith(text, end): - # usefull for paths which can be both, str and bytes - if isinstance(text, str): - if not isinstance(end, str): - end = end.decode("ascii") - else: - if not isinstance(end, bytes): - end = end.encode("ascii") - return text.endswith(end) - - iteritems = lambda d: iter(d.items()) - itervalues = lambda d: iter(d.values()) - iterkeys = lambda d: iter(d.keys()) - - iterbytes = lambda b: (bytes([v]) for v in b) - - def reraise(tp, value, tb): - raise tp(value).with_traceback(tb) - - def swap_to_string(cls): - return cls - - import builtins - builtins diff --git a/libs/common/mutagen/_constants.py b/libs/common/mutagen/_constants.py index 5c1c1a10..77282315 100644 --- a/libs/common/mutagen/_constants.py +++ b/libs/common/mutagen/_constants.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/libs/common/mutagen/_file.py b/libs/common/mutagen/_file.py index 2405a523..5c4c295a 100644 --- a/libs/common/mutagen/_file.py +++ b/libs/common/mutagen/_file.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -9,7 +8,6 @@ import warnings from mutagen._util import DictMixin, loadfile -from mutagen._compat import izip class FileType(DictMixin): @@ -97,7 +95,7 @@ class FileType(DictMixin): return self.tags.keys() @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. @@ -120,7 +118,7 @@ class FileType(DictMixin): return self.tags.delete(filething) @loadfile(writable=True) - def save(self, filething, **kwargs): + def save(self, filething=None, **kwargs): """save(filething=None, **kwargs) Save metadata tags. @@ -221,13 +219,13 @@ def File(filething, options=None, easy=False): filething (filething) options: Sequence of :class:`FileType` implementations, defaults to all included ones. - easy (bool): If the easy wrappers should be returnd if available. + easy (bool): If the easy wrappers should be returned if available. For example :class:`EasyMP3 ` instead of :class:`MP3 `. Returns: FileType: A FileType instance for the detected type or `None` in case - the type couln't be determined. + the type couldn't be determined. Raises: MutagenError: in case the detected type fails to load the file. @@ -264,12 +262,16 @@ def File(filething, options=None, easy=False): from mutagen.optimfrog import OptimFROG from mutagen.aiff import AIFF from mutagen.aac import AAC + from mutagen.ac3 import AC3 from mutagen.smf import SMF + from mutagen.tak import TAK from mutagen.dsf import DSF + from mutagen.dsdiff import DSDIFF + from mutagen.wave import WAVE options = [MP3, TrueAudio, OggTheora, OggSpeex, OggVorbis, OggFLAC, FLAC, AIFF, APEv2File, MP4, ID3FileType, WavPack, - Musepack, MonkeysAudio, OptimFROG, ASF, OggOpus, AAC, - SMF, DSF] + Musepack, MonkeysAudio, OptimFROG, ASF, OggOpus, AAC, AC3, + SMF, TAK, DSF, DSDIFF, WAVE] if not options: return None @@ -287,7 +289,7 @@ def File(filething, options=None, easy=False): results = [(Kind.score(filething.name, fileobj, header), Kind.__name__) for Kind in options] - results = list(izip(results, options)) + results = list(zip(results, options)) results.sort() (score, name), Kind = results[-1] if score > 0: diff --git a/libs/common/mutagen/_iff.py b/libs/common/mutagen/_iff.py new file mode 100644 index 00000000..01f14db6 --- /dev/null +++ b/libs/common/mutagen/_iff.py @@ -0,0 +1,386 @@ +# Copyright (C) 2014 Evan Purkhiser +# 2014 Ben Ockmore +# 2017 Borewit +# 2019-2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +"""Base classes for various IFF based formats (e.g. AIFF or RIFF).""" + +import sys + +from mutagen.id3 import ID3 +from mutagen.id3._util import ID3NoHeaderError, error as ID3Error +from mutagen._util import ( + MutagenError, + convert_error, + delete_bytes, + insert_bytes, + loadfile, + reraise, + resize_bytes, +) + + +class error(MutagenError): + pass + + +class InvalidChunk(error): + pass + + +class EmptyChunk(InvalidChunk): + pass + + +def is_valid_chunk_id(id): + """ is_valid_chunk_id(FOURCC) + + Arguments: + id (FOURCC) + Returns: + true if valid; otherwise false + + Check if argument id is valid FOURCC type. + """ + + assert isinstance(id, str), \ + 'id is of type %s, must be str: %r' % (type(id), id) + + return ((0 < len(id) <= 4) and (min(id) >= ' ') and + (max(id) <= '~')) + + +# Assert FOURCC formatted valid +def assert_valid_chunk_id(id): + if not is_valid_chunk_id(id): + raise ValueError("IFF chunk ID must be four ASCII characters.") + + +class IffChunk(object): + """Generic representation of a single IFF chunk. + + IFF chunks always consist of an ID followed by the chunk size. The exact + format varies between different IFF based formats, e.g. AIFF uses + big-endian while RIFF uses little-endian. + """ + + # Chunk headers are usually 8 bytes long (4 for ID and 4 for the size) + HEADER_SIZE = 8 + + @classmethod + def parse_header(cls, header): + """Read ID and data_size from the given header. + Must be implemented in subclasses.""" + raise error("Not implemented") + + def write_new_header(self, id_, size): + """Write the chunk header with id_ and size to the file. + Must be implemented in subclasses. The data must be written + to the current position in self._fileobj.""" + raise error("Not implemented") + + def write_size(self): + """Write self.data_size to the file. + Must be implemented in subclasses. The data must be written + to the current position in self._fileobj.""" + raise error("Not implemented") + + @classmethod + def get_class(cls, id): + """Returns the class for a new chunk for a given ID. + Can be overridden in subclasses to implement specific chunk types.""" + return cls + + @classmethod + def parse(cls, fileobj, parent_chunk=None): + header = fileobj.read(cls.HEADER_SIZE) + if len(header) < cls.HEADER_SIZE: + raise EmptyChunk('Header size < %i' % cls.HEADER_SIZE) + id, data_size = cls.parse_header(header) + try: + id = id.decode('ascii').rstrip() + except UnicodeDecodeError as e: + raise InvalidChunk(e) + + if not is_valid_chunk_id(id): + raise InvalidChunk('Invalid chunk ID %r' % id) + + return cls.get_class(id)(fileobj, id, data_size, parent_chunk) + + def __init__(self, fileobj, id, data_size, parent_chunk): + self._fileobj = fileobj + self.id = id + self.data_size = data_size + self.parent_chunk = parent_chunk + self.data_offset = fileobj.tell() + self.offset = self.data_offset - self.HEADER_SIZE + self._calculate_size() + + def __repr__(self): + return ("<%s id=%s, offset=%i, size=%i, data_offset=%i, data_size=%i>" + % (type(self).__name__, self.id, self.offset, self.size, + self.data_offset, self.data_size)) + + def read(self): + """Read the chunks data""" + + self._fileobj.seek(self.data_offset) + return self._fileobj.read(self.data_size) + + def write(self, data): + """Write the chunk data""" + + if len(data) > self.data_size: + raise ValueError + + self._fileobj.seek(self.data_offset) + self._fileobj.write(data) + # Write the padding bytes + padding = self.padding() + if padding: + self._fileobj.seek(self.data_offset + self.data_size) + self._fileobj.write(b'\x00' * padding) + + def delete(self): + """Removes the chunk from the file""" + + delete_bytes(self._fileobj, self.size, self.offset) + if self.parent_chunk is not None: + self.parent_chunk._remove_subchunk(self) + self._fileobj.flush() + + def _update_size(self, size_diff, changed_subchunk=None): + """Update the size of the chunk""" + + old_size = self.size + self.data_size += size_diff + self._fileobj.seek(self.offset + 4) + self.write_size() + self._calculate_size() + if self.parent_chunk is not None: + self.parent_chunk._update_size(self.size - old_size, self) + if changed_subchunk: + self._update_sibling_offsets( + changed_subchunk, old_size - self.size) + + def _calculate_size(self): + self.size = self.HEADER_SIZE + self.data_size + self.padding() + assert self.size % 2 == 0 + + def resize(self, new_data_size): + """Resize the file and update the chunk sizes""" + + padding = new_data_size % 2 + resize_bytes(self._fileobj, self.data_size + self.padding(), + new_data_size + padding, self.data_offset) + size_diff = new_data_size - self.data_size + self._update_size(size_diff) + self._fileobj.flush() + + def padding(self): + """Returns the number of padding bytes (0 or 1). + IFF chunks are required to be a even number in total length. If + data_size is odd a padding byte will be added at the end. + """ + return self.data_size % 2 + + +class IffContainerChunkMixin(): + """A IFF chunk containing other chunks. + + A container chunk can have an additional name as the first 4 bytes of the + chunk data followed by an arbitrary number of subchunks. The root chunk of + the file is always a container chunk (e.g. the AIFF chunk or the FORM chunk + for RIFF) but there can be other types of container chunks (e.g. the LIST + chunks used in RIFF). + """ + + def parse_next_subchunk(self): + """""" + raise error("Not implemented") + + def init_container(self, name_size=4): + # Lists can store an additional name identifier before the subchunks + self.__name_size = name_size + if self.data_size < name_size: + raise InvalidChunk( + 'Container chunk data size < %i' % name_size) + + # Read the container name + if name_size > 0: + try: + self.name = self._fileobj.read(name_size).decode('ascii') + except UnicodeDecodeError as e: + raise error(e) + else: + self.name = None + + # Load all IFF subchunks + self.__subchunks = [] + + def subchunks(self): + """Returns a list of all subchunks. + The list is lazily loaded on first access. + """ + if not self.__subchunks: + next_offset = self.data_offset + self.__name_size + while next_offset < self.offset + self.size: + self._fileobj.seek(next_offset) + try: + chunk = self.parse_next_subchunk() + except EmptyChunk: + break + except InvalidChunk: + break + self.__subchunks.append(chunk) + + # Calculate the location of the next chunk + next_offset = chunk.offset + chunk.size + return self.__subchunks + + def insert_chunk(self, id_, data=None): + """Insert a new chunk at the end of the container chunk""" + + if not is_valid_chunk_id(id_): + raise KeyError("Invalid IFF key.") + + next_offset = self.offset + self.size + size = self.HEADER_SIZE + data_size = 0 + if data: + data_size = len(data) + padding = data_size % 2 + size += data_size + padding + insert_bytes(self._fileobj, size, next_offset) + self._fileobj.seek(next_offset) + self.write_new_header(id_.ljust(4).encode('ascii'), data_size) + self._fileobj.seek(next_offset) + chunk = self.parse_next_subchunk() + self._update_size(chunk.size) + if data: + chunk.write(data) + self.subchunks().append(chunk) + self._fileobj.flush() + return chunk + + def __contains__(self, id_): + """Check if this chunk contains a specific subchunk.""" + assert_valid_chunk_id(id_) + try: + self[id_] + return True + except KeyError: + return False + + def __getitem__(self, id_): + """Get a subchunk by ID.""" + assert_valid_chunk_id(id_) + found_chunk = None + for chunk in self.subchunks(): + if chunk.id == id_: + found_chunk = chunk + break + else: + raise KeyError("No %r chunk found" % id_) + return found_chunk + + def __delitem__(self, id_): + """Remove a chunk from the IFF file""" + assert_valid_chunk_id(id_) + self[id_].delete() + + def _remove_subchunk(self, chunk): + assert chunk in self.__subchunks + self._update_size(-chunk.size, chunk) + self.__subchunks.remove(chunk) + + def _update_sibling_offsets(self, changed_subchunk, size_diff): + """Update the offsets of subchunks after `changed_subchunk`. + """ + index = self.__subchunks.index(changed_subchunk) + sibling_chunks = self.__subchunks[index + 1:len(self.__subchunks)] + for sibling in sibling_chunks: + sibling.offset -= size_diff + sibling.data_offset -= size_diff + + +class IffFile: + """Representation of a IFF file""" + + def __init__(self, chunk_cls, fileobj): + fileobj.seek(0) + self.root = chunk_cls.parse(fileobj) + + def __contains__(self, id_): + """Check if the IFF file contains a specific chunk""" + return id_ in self.root + + def __getitem__(self, id_): + """Get a chunk from the IFF file""" + return self.root[id_] + + def __delitem__(self, id_): + """Remove a chunk from the IFF file""" + self.delete_chunk(id_) + + def delete_chunk(self, id_): + """Remove a chunk from the IFF file""" + del self.root[id_] + + def insert_chunk(self, id_, data=None): + """Insert a new chunk at the end of the IFF file""" + return self.root.insert_chunk(id_, data) + + +class IffID3(ID3): + """A generic IFF file with ID3v2 tags""" + + def _load_file(self, fileobj): + raise error("Not implemented") + + def _pre_load_header(self, fileobj): + try: + fileobj.seek(self._load_file(fileobj)['ID3'].data_offset) + except (InvalidChunk, KeyError): + raise ID3NoHeaderError("No ID3 chunk") + + @convert_error(IOError, error) + @loadfile(writable=True) + def save(self, filething=None, v2_version=4, v23_sep='/', padding=None): + """Save ID3v2 data to the IFF file""" + + fileobj = filething.fileobj + + iff_file = self._load_file(fileobj) + + if 'ID3' not in iff_file: + iff_file.insert_chunk('ID3') + + chunk = iff_file['ID3'] + + try: + data = self._prepare_data( + fileobj, chunk.data_offset, chunk.data_size, v2_version, + v23_sep, padding) + except ID3Error as e: + reraise(error, e, sys.exc_info()[2]) + + chunk.resize(len(data)) + chunk.write(data) + + @convert_error(IOError, error) + @loadfile(writable=True) + def delete(self, filething=None): + """Completely removes the ID3 chunk from the IFF file""" + + try: + iff_file = self._load_file(filething.fileobj) + del iff_file['ID3'] + except KeyError: + pass + self.clear() diff --git a/libs/common/mutagen/_riff.py b/libs/common/mutagen/_riff.py new file mode 100644 index 00000000..f3f23f6a --- /dev/null +++ b/libs/common/mutagen/_riff.py @@ -0,0 +1,69 @@ +# Copyright (C) 2017 Borewit +# Copyright (C) 2019-2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +"""Resource Interchange File Format (RIFF).""" + +import struct +from struct import pack + +from mutagen._iff import ( + IffChunk, + IffContainerChunkMixin, + IffFile, + InvalidChunk, +) + + +class RiffChunk(IffChunk): + """Generic RIFF chunk""" + + @classmethod + def parse_header(cls, header): + return struct.unpack('<4sI', header) + + @classmethod + def get_class(cls, id): + if id in (u'LIST', u'RIFF'): + return RiffListChunk + else: + return cls + + def write_new_header(self, id_, size): + self._fileobj.write(pack('<4sI', id_, size)) + + def write_size(self): + self._fileobj.write(pack(' use our ctypes one as well - try: - del_windows_env_var(key) - except WindowsError: - pass - else: - os.unsetenv(key) - - -def putenv(key, value): - """Like `os.putenv` but takes unicode under Windows + Python 2 - - Args: - key (pathlike): The env var to get - value (pathlike): The value to set - Raises: - ValueError - """ - - key = path2fsn(key) - value = path2fsn(value) - - if is_win and PY2: - try: - set_windows_env_var(key, value) - except WindowsError: - # py3 + win fails here - raise ValueError - else: - try: - os.putenv(key, value) - except OSError: - # win + py3 raise here for invalid keys which is probably a bug. - # ValueError seems better - raise ValueError diff --git a/libs/common/mutagen/_senf/_fsnative.py b/libs/common/mutagen/_senf/_fsnative.py deleted file mode 100644 index a1e5967c..00000000 --- a/libs/common/mutagen/_senf/_fsnative.py +++ /dev/null @@ -1,666 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import os -import sys -import ctypes -import codecs - -from . import _winapi as winapi -from ._compat import text_type, PY3, PY2, urlparse, quote, unquote, urlunparse - - -is_win = os.name == "nt" -is_unix = not is_win -is_darwin = sys.platform == "darwin" - -_surrogatepass = "strict" if PY2 else "surrogatepass" - - -def _normalize_codec(codec, _cache={}): - """Raises LookupError""" - - try: - return _cache[codec] - except KeyError: - _cache[codec] = codecs.lookup(codec).name - return _cache[codec] - - -def _swap_bytes(data): - """swaps bytes for 16 bit, leaves remaining trailing bytes alone""" - - a, b = data[1::2], data[::2] - data = bytearray().join(bytearray(x) for x in zip(a, b)) - if len(b) > len(a): - data += b[-1:] - return bytes(data) - - -def _codec_fails_on_encode_surrogates(codec, _cache={}): - """Returns if a codec fails correctly when passing in surrogates with - a surrogatepass/surrogateescape error handler. Some codecs were broken - in Python <3.4 - """ - - try: - return _cache[codec] - except KeyError: - try: - u"\uD800\uDC01".encode(codec) - except UnicodeEncodeError: - _cache[codec] = True - else: - _cache[codec] = False - return _cache[codec] - - -def _codec_can_decode_with_surrogatepass(codec, _cache={}): - """Returns if a codec supports the surrogatepass error handler when - decoding. Some codecs were broken in Python <3.4 - """ - - try: - return _cache[codec] - except KeyError: - try: - u"\ud83d".encode( - codec, _surrogatepass).decode(codec, _surrogatepass) - except UnicodeDecodeError: - _cache[codec] = False - else: - _cache[codec] = True - return _cache[codec] - - -def _decode_surrogatepass(data, codec): - """Like data.decode(codec, 'surrogatepass') but makes utf-16-le/be work - on Python < 3.4 + Windows - - https://bugs.python.org/issue27971 - - Raises UnicodeDecodeError, LookupError - """ - - try: - return data.decode(codec, _surrogatepass) - except UnicodeDecodeError: - if not _codec_can_decode_with_surrogatepass(codec): - if _normalize_codec(codec) == "utf-16-be": - data = _swap_bytes(data) - codec = "utf-16-le" - if _normalize_codec(codec) == "utf-16-le": - buffer_ = ctypes.create_string_buffer(data + b"\x00\x00") - value = ctypes.wstring_at(buffer_, len(data) // 2) - if value.encode("utf-16-le", _surrogatepass) != data: - raise - return value - else: - raise - else: - raise - - -def _winpath2bytes_py3(text, codec): - """Fallback implementation for text including surrogates""" - - # merge surrogate codepoints - if _normalize_codec(codec).startswith("utf-16"): - # fast path, utf-16 merges anyway - return text.encode(codec, _surrogatepass) - return _decode_surrogatepass( - text.encode("utf-16-le", _surrogatepass), - "utf-16-le").encode(codec, _surrogatepass) - - -if PY2: - def _winpath2bytes(text, codec): - return text.encode(codec) -else: - def _winpath2bytes(text, codec): - if _codec_fails_on_encode_surrogates(codec): - try: - return text.encode(codec) - except UnicodeEncodeError: - return _winpath2bytes_py3(text, codec) - else: - return _winpath2bytes_py3(text, codec) - - -def fsn2norm(path): - """ - Args: - path (fsnative): The path to normalize - Returns: - `fsnative` - - Normalizes an fsnative path. - - The same underlying path can have multiple representations as fsnative - (due to surrogate pairs and variable length encodings). When concatenating - fsnative the result might be different than concatenating the serialized - form and then deserializing it. - - This returns the normalized form i.e. the form which os.listdir() would - return. This is useful when you alter fsnative but require that the same - underlying path always maps to the same fsnative value. - - All functions like :func:`bytes2fsn`, :func:`fsnative`, :func:`text2fsn` - and :func:`path2fsn` always return a normalized path, independent of their - input. - """ - - native = _fsn2native(path) - - if is_win: - return _decode_surrogatepass( - native.encode("utf-16-le", _surrogatepass), - "utf-16-le") - elif PY3: - return bytes2fsn(native, None) - else: - return path - - -def _fsn2legacy(path): - """Takes a fsnative path and returns a path that can be put into os.environ - or sys.argv. Might result in a mangled path on Python2 + Windows. - Can't fail. - - Args: - path (fsnative) - Returns: - str - """ - - if PY2 and is_win: - return path.encode(_encoding, "replace") - return path - - -def _fsnative(text): - if not isinstance(text, text_type): - raise TypeError("%r needs to be a text type (%r)" % (text, text_type)) - - if is_unix: - # First we go to bytes so we can be sure we have a valid source. - # Theoretically we should fail here in case we have a non-unicode - # encoding. But this would make everything complicated and there is - # no good way to handle a failure from the user side. Instead - # fall back to utf-8 which is the most likely the right choice in - # a mis-configured environment - encoding = _encoding - try: - path = text.encode(encoding, _surrogatepass) - except UnicodeEncodeError: - path = text.encode("utf-8", _surrogatepass) - - if b"\x00" in path: - path = path.replace(b"\x00", fsn2bytes(_fsnative(u"\uFFFD"), None)) - - if PY3: - return path.decode(_encoding, "surrogateescape") - return path - else: - if u"\x00" in text: - text = text.replace(u"\x00", u"\uFFFD") - text = fsn2norm(text) - return text - - -def _create_fsnative(type_): - # a bit of magic to make fsnative(u"foo") and isinstance(path, fsnative) - # work - - class meta(type): - - def __instancecheck__(self, instance): - return _typecheck_fsnative(instance) - - def __subclasscheck__(self, subclass): - return issubclass(subclass, type_) - - class impl(object): - """fsnative(text=u"") - - Args: - text (text): The text to convert to a path - Returns: - fsnative: The new path. - Raises: - TypeError: In case something other then `text` has been passed - - This type is a virtual base class for the real path type. - Instantiating it returns an instance of the real path type and it - overrides instance and subclass checks so that `isinstance` and - `issubclass` checks work: - - :: - - isinstance(fsnative(u"foo"), fsnative) == True - issubclass(type(fsnative(u"foo")), fsnative) == True - - The real returned type is: - - - **Python 2 + Windows:** :obj:`python:unicode`, with ``surrogates``, - without ``null`` - - **Python 2 + Unix:** :obj:`python:str`, without ``null`` - - **Python 3 + Windows:** :obj:`python3:str`, with ``surrogates``, - without ``null`` - - **Python 3 + Unix:** :obj:`python3:str`, with ``surrogates``, without - ``null``, without code points not encodable with the locale encoding - - Constructing a `fsnative` can't fail. - - Passing a `fsnative` to :func:`open` will never lead to `ValueError` - or `TypeError`. - - Any operation on `fsnative` can also use the `str` type, as long as - the `str` only contains ASCII and no NULL. - """ - - def __new__(cls, text=u""): - return _fsnative(text) - - new_type = meta("fsnative", (object,), dict(impl.__dict__)) - new_type.__module__ = "senf" - return new_type - - -fsnative_type = text_type if is_win or PY3 else bytes -fsnative = _create_fsnative(fsnative_type) - - -def _typecheck_fsnative(path): - """ - Args: - path (object) - Returns: - bool: if path is a fsnative - """ - - if not isinstance(path, fsnative_type): - return False - - if PY3 or is_win: - if u"\x00" in path: - return False - - if is_unix: - try: - path.encode(_encoding, "surrogateescape") - except UnicodeEncodeError: - return False - elif b"\x00" in path: - return False - - return True - - -def _fsn2native(path): - """ - Args: - path (fsnative) - Returns: - `text` on Windows, `bytes` on Unix - Raises: - TypeError: in case the type is wrong or the ´str` on Py3 + Unix - can't be converted to `bytes` - - This helper allows to validate the type and content of a path. - To reduce overhead the encoded value for Py3 + Unix is returned so - it can be reused. - """ - - if not isinstance(path, fsnative_type): - raise TypeError("path needs to be %s, not %s" % ( - fsnative_type.__name__, type(path).__name__)) - - if is_unix: - if PY3: - try: - path = path.encode(_encoding, "surrogateescape") - except UnicodeEncodeError: - # This look more like ValueError, but raising only one error - # makes things simpler... also one could say str + surrogates - # is its own type - raise TypeError( - "path contained Unicode code points not valid in" - "the current path encoding. To create a valid " - "path from Unicode use text2fsn()") - - if b"\x00" in path: - raise TypeError("fsnative can't contain nulls") - else: - if u"\x00" in path: - raise TypeError("fsnative can't contain nulls") - - return path - - -def _get_encoding(): - """The encoding used for paths, argv, environ, stdout and stdin""" - - encoding = sys.getfilesystemencoding() - if encoding is None: - if is_darwin: - encoding = "utf-8" - elif is_win: - encoding = "mbcs" - else: - encoding = "ascii" - encoding = _normalize_codec(encoding) - return encoding - - -_encoding = _get_encoding() - - -def path2fsn(path): - """ - Args: - path (pathlike): The path to convert - Returns: - `fsnative` - Raises: - TypeError: In case the type can't be converted to a `fsnative` - ValueError: In case conversion fails - - Returns a `fsnative` path for a `pathlike`. - """ - - # allow mbcs str on py2+win and bytes on py3 - if PY2: - if is_win: - if isinstance(path, bytes): - path = path.decode(_encoding) - else: - if isinstance(path, text_type): - path = path.encode(_encoding) - if "\x00" in path: - raise ValueError("embedded null") - else: - path = getattr(os, "fspath", lambda x: x)(path) - if isinstance(path, bytes): - if b"\x00" in path: - raise ValueError("embedded null") - path = path.decode(_encoding, "surrogateescape") - elif is_unix and isinstance(path, str): - # make sure we can encode it and this is not just some random - # unicode string - data = path.encode(_encoding, "surrogateescape") - if b"\x00" in data: - raise ValueError("embedded null") - path = fsn2norm(path) - else: - if u"\x00" in path: - raise ValueError("embedded null") - path = fsn2norm(path) - - if not isinstance(path, fsnative_type): - raise TypeError("path needs to be %s", fsnative_type.__name__) - - return path - - -def fsn2text(path, strict=False): - """ - Args: - path (fsnative): The path to convert - strict (bool): Fail in case the conversion is not reversible - Returns: - `text` - Raises: - TypeError: In case no `fsnative` has been passed - ValueError: In case ``strict`` was True and the conversion failed - - Converts a `fsnative` path to `text`. - - Can be used to pass a path to some unicode API, like for example a GUI - toolkit. - - If ``strict`` is True the conversion will fail in case it is not - reversible. This can be useful for converting program arguments that are - supposed to be text and erroring out in case they are not. - - Encoding with a Unicode encoding will always succeed with the result. - """ - - path = _fsn2native(path) - - errors = "strict" if strict else "replace" - - if is_win: - return path.encode("utf-16-le", _surrogatepass).decode("utf-16-le", - errors) - else: - return path.decode(_encoding, errors) - - -def text2fsn(text): - """ - Args: - text (text): The text to convert - Returns: - `fsnative` - Raises: - TypeError: In case no `text` has been passed - - Takes `text` and converts it to a `fsnative`. - - This operation is not reversible and can't fail. - """ - - return fsnative(text) - - -def fsn2bytes(path, encoding="utf-8"): - """ - Args: - path (fsnative): The path to convert - encoding (`str`): encoding used for Windows - Returns: - `bytes` - Raises: - TypeError: If no `fsnative` path is passed - ValueError: If encoding fails or the encoding is invalid - - Converts a `fsnative` path to `bytes`. - - The passed *encoding* is only used on platforms where paths are not - associated with an encoding (Windows for example). - - For Windows paths, lone surrogates will be encoded like normal code points - and surrogate pairs will be merged before encoding. In case of ``utf-8`` - or ``utf-16-le`` this is equal to the `WTF-8 and WTF-16 encoding - `__. - """ - - path = _fsn2native(path) - - if is_win: - if encoding is None: - raise ValueError("invalid encoding %r" % encoding) - - try: - return _winpath2bytes(path, encoding) - except LookupError: - raise ValueError("invalid encoding %r" % encoding) - else: - return path - - -def bytes2fsn(data, encoding="utf-8"): - """ - Args: - data (bytes): The data to convert - encoding (`str`): encoding used for Windows - Returns: - `fsnative` - Raises: - TypeError: If no `bytes` path is passed - ValueError: If decoding fails or the encoding is invalid - - Turns `bytes` to a `fsnative` path. - - The passed *encoding* is only used on platforms where paths are not - associated with an encoding (Windows for example). - - For Windows paths ``WTF-8`` is accepted if ``utf-8`` is used and - ``WTF-16`` accepted if ``utf-16-le`` is used. - """ - - if not isinstance(data, bytes): - raise TypeError("data needs to be bytes") - - if is_win: - if encoding is None: - raise ValueError("invalid encoding %r" % encoding) - try: - path = _decode_surrogatepass(data, encoding) - except LookupError: - raise ValueError("invalid encoding %r" % encoding) - if u"\x00" in path: - raise ValueError("contains nulls") - return path - else: - if b"\x00" in data: - raise ValueError("contains nulls") - if PY2: - return data - else: - return data.decode(_encoding, "surrogateescape") - - -def uri2fsn(uri): - """ - Args: - uri (`text` or :obj:`python:str`): A file URI - Returns: - `fsnative` - Raises: - TypeError: In case an invalid type is passed - ValueError: In case the URI isn't a valid file URI - - Takes a file URI and returns a `fsnative` path - """ - - if PY2: - if isinstance(uri, text_type): - uri = uri.encode("utf-8") - if not isinstance(uri, bytes): - raise TypeError("uri needs to be ascii str or unicode") - else: - if not isinstance(uri, str): - raise TypeError("uri needs to be str") - - parsed = urlparse(uri) - scheme = parsed.scheme - netloc = parsed.netloc - path = parsed.path - - if scheme != "file": - raise ValueError("Not a file URI: %r" % uri) - - if not path: - raise ValueError("Invalid file URI: %r" % uri) - - uri = urlunparse(parsed)[7:] - - if is_win: - try: - drive, rest = uri.split(":", 1) - except ValueError: - path = "" - rest = uri.replace("/", "\\") - else: - path = drive[-1] + ":" - rest = rest.replace("/", "\\") - if PY2: - path += unquote(rest) - else: - path += unquote(rest, encoding="utf-8", errors="surrogatepass") - if netloc: - path = "\\\\" + path - if PY2: - path = path.decode("utf-8") - if u"\x00" in path: - raise ValueError("embedded null") - return path - else: - if PY2: - path = unquote(uri) - else: - path = unquote(uri, encoding=_encoding, errors="surrogateescape") - if "\x00" in path: - raise ValueError("embedded null") - return path - - -def fsn2uri(path): - """ - Args: - path (fsnative): The path to convert to an URI - Returns: - `text`: An ASCII only URI - Raises: - TypeError: If no `fsnative` was passed - ValueError: If the path can't be converted - - Takes a `fsnative` path and returns a file URI. - - On Windows non-ASCII characters will be encoded using utf-8 and then - percent encoded. - """ - - path = _fsn2native(path) - - def _quote_path(path): - # RFC 2396 - path = quote(path, "/:@&=+$,") - if PY2: - path = path.decode("ascii") - return path - - if is_win: - buf = ctypes.create_unicode_buffer(winapi.INTERNET_MAX_URL_LENGTH) - length = winapi.DWORD(winapi.INTERNET_MAX_URL_LENGTH) - flags = 0 - try: - winapi.UrlCreateFromPathW(path, buf, ctypes.byref(length), flags) - except WindowsError as e: - raise ValueError(e) - uri = buf[:length.value] - - # For some reason UrlCreateFromPathW escapes some chars outside of - # ASCII and some not. Unquote and re-quote with utf-8. - if PY3: - # latin-1 maps code points directly to bytes, which is what we want - uri = unquote(uri, "latin-1") - else: - # Python 2 does what we want by default - uri = unquote(uri) - - return _quote_path(uri.encode("utf-8", _surrogatepass)) - - else: - return u"file://" + _quote_path(path) diff --git a/libs/common/mutagen/_senf/_print.py b/libs/common/mutagen/_senf/_print.py deleted file mode 100644 index 63c50fa5..00000000 --- a/libs/common/mutagen/_senf/_print.py +++ /dev/null @@ -1,424 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import sys -import os -import ctypes -import re - -from ._fsnative import _encoding, is_win, is_unix, _surrogatepass, bytes2fsn -from ._compat import text_type, PY2, PY3 -from ._winansi import AnsiState, ansi_split -from . import _winapi as winapi - - -def print_(*objects, **kwargs): - """print_(*objects, sep=None, end=None, file=None, flush=False) - - Args: - objects (object): zero or more objects to print - sep (str): Object separator to use, defaults to ``" "`` - end (str): Trailing string to use, defaults to ``"\\n"``. - If end is ``"\\n"`` then `os.linesep` is used. - file (object): A file-like object, defaults to `sys.stdout` - flush (bool): If the file stream should be flushed - Raises: - EnvironmentError - - Like print(), but: - - * Supports printing filenames under Unix + Python 3 and Windows + Python 2 - * Emulates ANSI escape sequence support under Windows - * Never fails due to encoding/decoding errors. Tries hard to get everything - on screen as is, but will fall back to "?" if all fails. - - This does not conflict with ``colorama``, but will not use it on Windows. - """ - - sep = kwargs.get("sep") - sep = sep if sep is not None else " " - end = kwargs.get("end") - end = end if end is not None else "\n" - file = kwargs.get("file") - file = file if file is not None else sys.stdout - flush = bool(kwargs.get("flush", False)) - - if is_win: - _print_windows(objects, sep, end, file, flush) - else: - _print_unix(objects, sep, end, file, flush) - - -def _print_unix(objects, sep, end, file, flush): - """A print_() implementation which writes bytes""" - - encoding = _encoding - - if isinstance(sep, text_type): - sep = sep.encode(encoding, "replace") - if not isinstance(sep, bytes): - raise TypeError - - if isinstance(end, text_type): - end = end.encode(encoding, "replace") - if not isinstance(end, bytes): - raise TypeError - - if end == b"\n": - end = os.linesep - if PY3: - end = end.encode("ascii") - - parts = [] - for obj in objects: - if not isinstance(obj, text_type) and not isinstance(obj, bytes): - obj = text_type(obj) - if isinstance(obj, text_type): - if PY2: - obj = obj.encode(encoding, "replace") - else: - try: - obj = obj.encode(encoding, "surrogateescape") - except UnicodeEncodeError: - obj = obj.encode(encoding, "replace") - assert isinstance(obj, bytes) - parts.append(obj) - - data = sep.join(parts) + end - assert isinstance(data, bytes) - - file = getattr(file, "buffer", file) - - try: - file.write(data) - except TypeError: - if PY3: - # For StringIO, first try with surrogates - surr_data = data.decode(encoding, "surrogateescape") - try: - file.write(surr_data) - except (TypeError, ValueError): - file.write(data.decode(encoding, "replace")) - else: - # for file like objects with don't support bytes - file.write(data.decode(encoding, "replace")) - - if flush: - file.flush() - - -ansi_state = AnsiState() - - -def _print_windows(objects, sep, end, file, flush): - """The windows implementation of print_()""" - - h = winapi.INVALID_HANDLE_VALUE - - try: - fileno = file.fileno() - except (EnvironmentError, AttributeError): - pass - else: - if fileno == 1: - h = winapi.GetStdHandle(winapi.STD_OUTPUT_HANDLE) - elif fileno == 2: - h = winapi.GetStdHandle(winapi.STD_ERROR_HANDLE) - - encoding = _encoding - - parts = [] - for obj in objects: - if isinstance(obj, bytes): - obj = obj.decode(encoding, "replace") - if not isinstance(obj, text_type): - obj = text_type(obj) - parts.append(obj) - - if isinstance(sep, bytes): - sep = sep.decode(encoding, "replace") - if not isinstance(sep, text_type): - raise TypeError - - if isinstance(end, bytes): - end = end.decode(encoding, "replace") - if not isinstance(end, text_type): - raise TypeError - - if end == u"\n": - end = os.linesep - - text = sep.join(parts) + end - assert isinstance(text, text_type) - - is_console = True - if h == winapi.INVALID_HANDLE_VALUE: - is_console = False - else: - # get the default value - info = winapi.CONSOLE_SCREEN_BUFFER_INFO() - if not winapi.GetConsoleScreenBufferInfo(h, ctypes.byref(info)): - is_console = False - - if is_console: - # make sure we flush before we apply any console attributes - file.flush() - - # try to force a utf-8 code page, use the output CP if that fails - cp = winapi.GetConsoleOutputCP() - try: - encoding = "utf-8" - if winapi.SetConsoleOutputCP(65001) == 0: - encoding = None - - for is_ansi, part in ansi_split(text): - if is_ansi: - ansi_state.apply(h, part) - else: - if encoding is not None: - data = part.encode(encoding, _surrogatepass) - else: - data = _encode_codepage(cp, part) - os.write(fileno, data) - finally: - # reset the code page to what we had before - winapi.SetConsoleOutputCP(cp) - else: - # try writing bytes first, so in case of Python 2 StringIO we get - # the same type on all platforms - try: - file.write(text.encode("utf-8", _surrogatepass)) - except (TypeError, ValueError): - file.write(text) - - if flush: - file.flush() - - -def _readline_windows(): - """Raises OSError""" - - try: - fileno = sys.stdin.fileno() - except (EnvironmentError, AttributeError): - fileno = -1 - - # In case stdin is replaced, read from that - if fileno != 0: - return _readline_windows_fallback() - - h = winapi.GetStdHandle(winapi.STD_INPUT_HANDLE) - if h == winapi.INVALID_HANDLE_VALUE: - return _readline_windows_fallback() - - buf_size = 1024 - buf = ctypes.create_string_buffer(buf_size * ctypes.sizeof(winapi.WCHAR)) - read = winapi.DWORD() - - text = u"" - while True: - if winapi.ReadConsoleW( - h, buf, buf_size, ctypes.byref(read), None) == 0: - if not text: - return _readline_windows_fallback() - raise ctypes.WinError() - data = buf[:read.value * ctypes.sizeof(winapi.WCHAR)] - text += data.decode("utf-16-le", _surrogatepass) - if text.endswith(u"\r\n"): - return text[:-2] - - -def _decode_codepage(codepage, data): - """ - Args: - codepage (int) - data (bytes) - Returns: - `text` - - Decodes data using the given codepage. If some data can't be decoded - using the codepage it will not fail. - """ - - assert isinstance(data, bytes) - - if not data: - return u"" - - # get the required buffer length first - length = winapi.MultiByteToWideChar(codepage, 0, data, len(data), None, 0) - if length == 0: - raise ctypes.WinError() - - # now decode - buf = ctypes.create_unicode_buffer(length) - length = winapi.MultiByteToWideChar( - codepage, 0, data, len(data), buf, length) - if length == 0: - raise ctypes.WinError() - - return buf[:] - - -def _encode_codepage(codepage, text): - """ - Args: - codepage (int) - text (text) - Returns: - `bytes` - - Encode text using the given code page. Will not fail if a char - can't be encoded using that codepage. - """ - - assert isinstance(text, text_type) - - if not text: - return b"" - - size = (len(text.encode("utf-16-le", _surrogatepass)) // - ctypes.sizeof(winapi.WCHAR)) - - # get the required buffer size - length = winapi.WideCharToMultiByte( - codepage, 0, text, size, None, 0, None, None) - if length == 0: - raise ctypes.WinError() - - # decode to the buffer - buf = ctypes.create_string_buffer(length) - length = winapi.WideCharToMultiByte( - codepage, 0, text, size, buf, length, None, None) - if length == 0: - raise ctypes.WinError() - return buf[:length] - - -def _readline_windows_fallback(): - # In case reading from the console failed (maybe we get piped data) - # we assume the input was generated according to the output encoding. - # Got any better ideas? - assert is_win - cp = winapi.GetConsoleOutputCP() - data = getattr(sys.stdin, "buffer", sys.stdin).readline().rstrip(b"\r\n") - return _decode_codepage(cp, data) - - -def _readline_default(): - assert is_unix - data = getattr(sys.stdin, "buffer", sys.stdin).readline().rstrip(b"\r\n") - if PY3: - return data.decode(_encoding, "surrogateescape") - else: - return data - - -def _readline(): - if is_win: - return _readline_windows() - else: - return _readline_default() - - -def input_(prompt=None): - """ - Args: - prompt (object): Prints the passed object to stdout without - adding a trailing newline - Returns: - `fsnative` - Raises: - EnvironmentError - - Like :func:`python3:input` but returns a `fsnative` and allows printing - filenames as prompt to stdout. - - Use :func:`fsn2text` on the result if you just want to deal with text. - """ - - if prompt is not None: - print_(prompt, end="") - - return _readline() - - -def _get_file_name_for_handle(handle): - """(Windows only) Returns a file name for a file handle. - - Args: - handle (winapi.HANDLE) - Returns: - `text` or `None` if no file name could be retrieved. - """ - - assert is_win - assert handle != winapi.INVALID_HANDLE_VALUE - - size = winapi.FILE_NAME_INFO.FileName.offset + \ - winapi.MAX_PATH * ctypes.sizeof(winapi.WCHAR) - buf = ctypes.create_string_buffer(size) - - if winapi.GetFileInformationByHandleEx is None: - # Windows XP - return None - - status = winapi.GetFileInformationByHandleEx( - handle, winapi.FileNameInfo, buf, size) - if status == 0: - return None - - name_info = ctypes.cast( - buf, ctypes.POINTER(winapi.FILE_NAME_INFO)).contents - offset = winapi.FILE_NAME_INFO.FileName.offset - data = buf[offset:offset + name_info.FileNameLength] - return bytes2fsn(data, "utf-16-le") - - -def supports_ansi_escape_codes(fd): - """Returns whether the output device is capable of interpreting ANSI escape - codes when :func:`print_` is used. - - Args: - fd (int): file descriptor (e.g. ``sys.stdout.fileno()``) - Returns: - `bool` - """ - - if os.isatty(fd): - return True - - if not is_win: - return False - - # Check for cygwin/msys terminal - handle = winapi._get_osfhandle(fd) - if handle == winapi.INVALID_HANDLE_VALUE: - return False - - if winapi.GetFileType(handle) != winapi.FILE_TYPE_PIPE: - return False - - file_name = _get_file_name_for_handle(handle) - match = re.match( - "^\\\\(cygwin|msys)-[a-z0-9]+-pty[0-9]+-(from|to)-master$", file_name) - return match is not None diff --git a/libs/common/mutagen/_senf/_stdlib.py b/libs/common/mutagen/_senf/_stdlib.py deleted file mode 100644 index f3193d33..00000000 --- a/libs/common/mutagen/_senf/_stdlib.py +++ /dev/null @@ -1,154 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import re -import os - -from ._fsnative import path2fsn, fsnative, is_win -from ._compat import PY2 -from ._environ import environ - - -sep = path2fsn(os.sep) -pathsep = path2fsn(os.pathsep) -curdir = path2fsn(os.curdir) -pardir = path2fsn(os.pardir) -altsep = path2fsn(os.altsep) if os.altsep is not None else None -extsep = path2fsn(os.extsep) -devnull = path2fsn(os.devnull) -defpath = path2fsn(os.defpath) - - -def getcwd(): - """Like `os.getcwd` but returns a `fsnative` path - - Returns: - `fsnative` - """ - - if is_win and PY2: - return os.getcwdu() - return os.getcwd() - - -def _get_userdir(user=None): - """Returns the user dir or None""" - - if user is not None and not isinstance(user, fsnative): - raise TypeError - - if is_win: - if "HOME" in environ: - path = environ["HOME"] - elif "USERPROFILE" in environ: - path = environ["USERPROFILE"] - elif "HOMEPATH" in environ and "HOMEDRIVE" in environ: - path = os.path.join(environ["HOMEDRIVE"], environ["HOMEPATH"]) - else: - return - - if user is None: - return path - else: - return os.path.join(os.path.dirname(path), user) - else: - import pwd - - if user is None: - if "HOME" in environ: - return environ["HOME"] - else: - try: - return path2fsn(pwd.getpwuid(os.getuid()).pw_dir) - except KeyError: - return - else: - try: - return path2fsn(pwd.getpwnam(user).pw_dir) - except KeyError: - return - - -def expanduser(path): - """ - Args: - path (pathlike): A path to expand - Returns: - `fsnative` - - Like :func:`python:os.path.expanduser` but supports unicode home - directories under Windows + Python 2 and always returns a `fsnative`. - """ - - path = path2fsn(path) - - if path == "~": - return _get_userdir() - elif path.startswith("~" + sep) or ( - altsep is not None and path.startswith("~" + altsep)): - userdir = _get_userdir() - if userdir is None: - return path - return userdir + path[1:] - elif path.startswith("~"): - sep_index = path.find(sep) - if altsep is not None: - alt_index = path.find(altsep) - if alt_index != -1 and alt_index < sep_index: - sep_index = alt_index - - if sep_index == -1: - user = path[1:] - rest = "" - else: - user = path[1:sep_index] - rest = path[sep_index:] - - userdir = _get_userdir(user) - if userdir is not None: - return userdir + rest - else: - return path - else: - return path - - -def expandvars(path): - """ - Args: - path (pathlike): A path to expand - Returns: - `fsnative` - - Like :func:`python:os.path.expandvars` but supports unicode under Windows - + Python 2 and always returns a `fsnative`. - """ - - path = path2fsn(path) - - def repl_func(match): - return environ.get(match.group(1), match.group(0)) - - path = re.compile(r"\$(\w+)", flags=re.UNICODE).sub(repl_func, path) - if os.name == "nt": - path = re.sub(r"%([^%]+)%", repl_func, path) - return re.sub(r"\$\{([^\}]+)\}", repl_func, path) diff --git a/libs/common/mutagen/_senf/_temp.py b/libs/common/mutagen/_senf/_temp.py deleted file mode 100644 index d29b7217..00000000 --- a/libs/common/mutagen/_senf/_temp.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import tempfile - -from ._fsnative import path2fsn, fsnative - - -def gettempdir(): - """ - Returns: - `fsnative` - - Like :func:`python3:tempfile.gettempdir`, but always returns a `fsnative` - path - """ - - # FIXME: I don't want to reimplement all that logic, reading env vars etc. - # At least for the default it works. - return path2fsn(tempfile.gettempdir()) - - -def gettempprefix(): - """ - Returns: - `fsnative` - - Like :func:`python3:tempfile.gettempprefix`, but always returns a - `fsnative` path - """ - - return path2fsn(tempfile.gettempprefix()) - - -def mkstemp(suffix=None, prefix=None, dir=None, text=False): - """ - Args: - suffix (`pathlike` or `None`): suffix or `None` to use the default - prefix (`pathlike` or `None`): prefix or `None` to use the default - dir (`pathlike` or `None`): temp dir or `None` to use the default - text (bool): if the file should be opened in text mode - Returns: - Tuple[`int`, `fsnative`]: - A tuple containing the file descriptor and the file path - Raises: - EnvironmentError - - Like :func:`python3:tempfile.mkstemp` but always returns a `fsnative` - path. - """ - - suffix = fsnative() if suffix is None else path2fsn(suffix) - prefix = gettempprefix() if prefix is None else path2fsn(prefix) - dir = gettempdir() if dir is None else path2fsn(dir) - - return tempfile.mkstemp(suffix, prefix, dir, text) - - -def mkdtemp(suffix=None, prefix=None, dir=None): - """ - Args: - suffix (`pathlike` or `None`): suffix or `None` to use the default - prefix (`pathlike` or `None`): prefix or `None` to use the default - dir (`pathlike` or `None`): temp dir or `None` to use the default - Returns: - `fsnative`: A path to a directory - Raises: - EnvironmentError - - Like :func:`python3:tempfile.mkstemp` but always returns a `fsnative` path. - """ - - suffix = fsnative() if suffix is None else path2fsn(suffix) - prefix = gettempprefix() if prefix is None else path2fsn(prefix) - dir = gettempdir() if dir is None else path2fsn(dir) - - return tempfile.mkdtemp(suffix, prefix, dir) diff --git a/libs/common/mutagen/_senf/_winansi.py b/libs/common/mutagen/_senf/_winansi.py deleted file mode 100644 index fbbc1c22..00000000 --- a/libs/common/mutagen/_senf/_winansi.py +++ /dev/null @@ -1,319 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import ctypes -import re -import atexit - -from . import _winapi as winapi - - -def ansi_parse(code): - """Returns command, (args)""" - - return code[-1:], tuple([int(v or "0") for v in code[2:-1].split(";")]) - - -def ansi_split(text, _re=re.compile(u"(\x1b\\[(\\d*;?)*\\S)")): - """Yields (is_ansi, text)""" - - for part in _re.split(text): - if part: - yield (bool(_re.match(part)), part) - - -class AnsiCommand(object): - TEXT = "m" - - MOVE_UP = "A" - MOVE_DOWN = "B" - MOVE_FORWARD = "C" - MOVE_BACKWARD = "D" - - SET_POS = "H" - SET_POS_ALT = "f" - - SAVE_POS = "s" - RESTORE_POS = "u" - - -class TextAction(object): - RESET_ALL = 0 - - SET_BOLD = 1 - SET_DIM = 2 - SET_ITALIC = 3 - SET_UNDERLINE = 4 - SET_BLINK = 5 - SET_BLINK_FAST = 6 - SET_REVERSE = 7 - SET_HIDDEN = 8 - - RESET_BOLD = 21 - RESET_DIM = 22 - RESET_ITALIC = 23 - RESET_UNDERLINE = 24 - RESET_BLINK = 25 - RESET_BLINK_FAST = 26 - RESET_REVERSE = 27 - RESET_HIDDEN = 28 - - FG_BLACK = 30 - FG_RED = 31 - FG_GREEN = 32 - FG_YELLOW = 33 - FG_BLUE = 34 - FG_MAGENTA = 35 - FG_CYAN = 36 - FG_WHITE = 37 - - FG_DEFAULT = 39 - - FG_LIGHT_BLACK = 90 - FG_LIGHT_RED = 91 - FG_LIGHT_GREEN = 92 - FG_LIGHT_YELLOW = 93 - FG_LIGHT_BLUE = 94 - FG_LIGHT_MAGENTA = 95 - FG_LIGHT_CYAN = 96 - FG_LIGHT_WHITE = 97 - - BG_BLACK = 40 - BG_RED = 41 - BG_GREEN = 42 - BG_YELLOW = 43 - BG_BLUE = 44 - BG_MAGENTA = 45 - BG_CYAN = 46 - BG_WHITE = 47 - - BG_DEFAULT = 49 - - BG_LIGHT_BLACK = 100 - BG_LIGHT_RED = 101 - BG_LIGHT_GREEN = 102 - BG_LIGHT_YELLOW = 103 - BG_LIGHT_BLUE = 104 - BG_LIGHT_MAGENTA = 105 - BG_LIGHT_CYAN = 106 - BG_LIGHT_WHITE = 107 - - -class AnsiState(object): - - def __init__(self): - self.default_attrs = None - - self.bold = False - self.bg_light = False - self.fg_light = False - - self.saved_pos = (0, 0) - - def do_text_action(self, attrs, action): - # In case the external state has changed, apply it it to ours. - # Mostly the first time this is called. - if attrs & winapi.FOREGROUND_INTENSITY and not self.fg_light \ - and not self.bold: - self.fg_light = True - if attrs & winapi.BACKGROUND_INTENSITY and not self.bg_light: - self.bg_light = True - - dark_fg = { - TextAction.FG_BLACK: 0, - TextAction.FG_RED: winapi.FOREGROUND_RED, - TextAction.FG_GREEN: winapi.FOREGROUND_GREEN, - TextAction.FG_YELLOW: - winapi.FOREGROUND_GREEN | winapi.FOREGROUND_RED, - TextAction.FG_BLUE: winapi.FOREGROUND_BLUE, - TextAction.FG_MAGENTA: winapi.FOREGROUND_BLUE | - winapi.FOREGROUND_RED, - TextAction.FG_CYAN: - winapi.FOREGROUND_BLUE | winapi.FOREGROUND_GREEN, - TextAction.FG_WHITE: - winapi.FOREGROUND_BLUE | winapi.FOREGROUND_GREEN | - winapi.FOREGROUND_RED, - } - - dark_bg = { - TextAction.BG_BLACK: 0, - TextAction.BG_RED: winapi.BACKGROUND_RED, - TextAction.BG_GREEN: winapi.BACKGROUND_GREEN, - TextAction.BG_YELLOW: - winapi.BACKGROUND_GREEN | winapi.BACKGROUND_RED, - TextAction.BG_BLUE: winapi.BACKGROUND_BLUE, - TextAction.BG_MAGENTA: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_RED, - TextAction.BG_CYAN: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_GREEN, - TextAction.BG_WHITE: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_GREEN | - winapi.BACKGROUND_RED, - } - - light_fg = { - TextAction.FG_LIGHT_BLACK: 0, - TextAction.FG_LIGHT_RED: winapi.FOREGROUND_RED, - TextAction.FG_LIGHT_GREEN: winapi.FOREGROUND_GREEN, - TextAction.FG_LIGHT_YELLOW: - winapi.FOREGROUND_GREEN | winapi.FOREGROUND_RED, - TextAction.FG_LIGHT_BLUE: winapi.FOREGROUND_BLUE, - TextAction.FG_LIGHT_MAGENTA: - winapi.FOREGROUND_BLUE | winapi.FOREGROUND_RED, - TextAction.FG_LIGHT_CYAN: - winapi.FOREGROUND_BLUE | winapi.FOREGROUND_GREEN, - TextAction.FG_LIGHT_WHITE: - winapi.FOREGROUND_BLUE | winapi.FOREGROUND_GREEN | - winapi.FOREGROUND_RED, - } - - light_bg = { - TextAction.BG_LIGHT_BLACK: 0, - TextAction.BG_LIGHT_RED: winapi.BACKGROUND_RED, - TextAction.BG_LIGHT_GREEN: winapi.BACKGROUND_GREEN, - TextAction.BG_LIGHT_YELLOW: - winapi.BACKGROUND_GREEN | winapi.BACKGROUND_RED, - TextAction.BG_LIGHT_BLUE: winapi.BACKGROUND_BLUE, - TextAction.BG_LIGHT_MAGENTA: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_RED, - TextAction.BG_LIGHT_CYAN: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_GREEN, - TextAction.BG_LIGHT_WHITE: - winapi.BACKGROUND_BLUE | winapi.BACKGROUND_GREEN | - winapi.BACKGROUND_RED, - } - - if action == TextAction.RESET_ALL: - attrs = self.default_attrs - self.bold = self.fg_light = self.bg_light = False - elif action == TextAction.SET_BOLD: - self.bold = True - elif action == TextAction.RESET_BOLD: - self.bold = False - elif action == TextAction.SET_DIM: - self.bold = False - elif action == TextAction.SET_REVERSE: - attrs |= winapi.COMMON_LVB_REVERSE_VIDEO - elif action == TextAction.RESET_REVERSE: - attrs &= ~winapi.COMMON_LVB_REVERSE_VIDEO - elif action == TextAction.SET_UNDERLINE: - attrs |= winapi.COMMON_LVB_UNDERSCORE - elif action == TextAction.RESET_UNDERLINE: - attrs &= ~winapi.COMMON_LVB_UNDERSCORE - elif action == TextAction.FG_DEFAULT: - attrs = (attrs & ~0xF) | (self.default_attrs & 0xF) - self.fg_light = False - elif action == TextAction.BG_DEFAULT: - attrs = (attrs & ~0xF0) | (self.default_attrs & 0xF0) - self.bg_light = False - elif action in dark_fg: - attrs = (attrs & ~0xF) | dark_fg[action] - self.fg_light = False - elif action in dark_bg: - attrs = (attrs & ~0xF0) | dark_bg[action] - self.bg_light = False - elif action in light_fg: - attrs = (attrs & ~0xF) | light_fg[action] - self.fg_light = True - elif action in light_bg: - attrs = (attrs & ~0xF0) | light_bg[action] - self.bg_light = True - - if self.fg_light or self.bold: - attrs |= winapi.FOREGROUND_INTENSITY - else: - attrs &= ~winapi.FOREGROUND_INTENSITY - - if self.bg_light: - attrs |= winapi.BACKGROUND_INTENSITY - else: - attrs &= ~winapi.BACKGROUND_INTENSITY - - return attrs - - def apply(self, handle, code): - buffer_info = winapi.CONSOLE_SCREEN_BUFFER_INFO() - if not winapi.GetConsoleScreenBufferInfo(handle, - ctypes.byref(buffer_info)): - return - - attrs = buffer_info.wAttributes - - # We take the first attrs we see as default - if self.default_attrs is None: - self.default_attrs = attrs - # Make sure that like with linux terminals the program doesn't - # affect the prompt after it exits - atexit.register( - winapi.SetConsoleTextAttribute, handle, self.default_attrs) - - cmd, args = ansi_parse(code) - if cmd == AnsiCommand.TEXT: - for action in args: - attrs = self.do_text_action(attrs, action) - winapi.SetConsoleTextAttribute(handle, attrs) - elif cmd in (AnsiCommand.MOVE_UP, AnsiCommand.MOVE_DOWN, - AnsiCommand.MOVE_FORWARD, AnsiCommand.MOVE_BACKWARD): - - coord = buffer_info.dwCursorPosition - x, y = coord.X, coord.Y - - amount = max(args[0], 1) - - if cmd == AnsiCommand.MOVE_UP: - y -= amount - elif cmd == AnsiCommand.MOVE_DOWN: - y += amount - elif cmd == AnsiCommand.MOVE_FORWARD: - x += amount - elif cmd == AnsiCommand.MOVE_BACKWARD: - x -= amount - - x = max(x, 0) - y = max(y, 0) - winapi.SetConsoleCursorPosition(handle, winapi.COORD(x, y)) - elif cmd in (AnsiCommand.SET_POS, AnsiCommand.SET_POS_ALT): - args = list(args) - while len(args) < 2: - args.append(0) - x, y = args[:2] - - win_rect = buffer_info.srWindow - x += win_rect.Left - 1 - y += win_rect.Top - 1 - - x = max(x, 0) - y = max(y, 0) - winapi.SetConsoleCursorPosition(handle, winapi.COORD(x, y)) - elif cmd == AnsiCommand.SAVE_POS: - win_rect = buffer_info.srWindow - coord = buffer_info.dwCursorPosition - x, y = coord.X, coord.Y - x -= win_rect.Left - y -= win_rect.Top - self.saved_pos = (x, y) - elif cmd == AnsiCommand.RESTORE_POS: - win_rect = buffer_info.srWindow - x, y = self.saved_pos - x += win_rect.Left - y += win_rect.Top - winapi.SetConsoleCursorPosition(handle, winapi.COORD(x, y)) diff --git a/libs/common/mutagen/_senf/_winapi.py b/libs/common/mutagen/_senf/_winapi.py deleted file mode 100644 index 5e0f7854..00000000 --- a/libs/common/mutagen/_senf/_winapi.py +++ /dev/null @@ -1,222 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Christoph Reiter -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import ctypes -from ctypes import WinDLL, CDLL, wintypes - - -shell32 = WinDLL("shell32") -kernel32 = WinDLL("kernel32") -shlwapi = WinDLL("shlwapi") -msvcrt = CDLL("msvcrt") - -GetCommandLineW = kernel32.GetCommandLineW -GetCommandLineW.argtypes = [] -GetCommandLineW.restype = wintypes.LPCWSTR - -CommandLineToArgvW = shell32.CommandLineToArgvW -CommandLineToArgvW.argtypes = [ - wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int)] -CommandLineToArgvW.restype = ctypes.POINTER(wintypes.LPWSTR) - -LocalFree = kernel32.LocalFree -LocalFree.argtypes = [wintypes.HLOCAL] -LocalFree.restype = wintypes.HLOCAL - -# https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751.aspx -LPCTSTR = ctypes.c_wchar_p -LPWSTR = wintypes.LPWSTR -LPCWSTR = ctypes.c_wchar_p -LPTSTR = LPWSTR -PCWSTR = ctypes.c_wchar_p -PCTSTR = PCWSTR -PWSTR = ctypes.c_wchar_p -PTSTR = PWSTR -LPVOID = wintypes.LPVOID -WCHAR = wintypes.WCHAR -LPSTR = ctypes.c_char_p - -BOOL = wintypes.BOOL -LPBOOL = ctypes.POINTER(BOOL) -UINT = wintypes.UINT -WORD = wintypes.WORD -DWORD = wintypes.DWORD -SHORT = wintypes.SHORT -HANDLE = wintypes.HANDLE -ULONG = wintypes.ULONG -LPCSTR = wintypes.LPCSTR - -STD_INPUT_HANDLE = DWORD(-10) -STD_OUTPUT_HANDLE = DWORD(-11) -STD_ERROR_HANDLE = DWORD(-12) - -INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value - -INTERNET_MAX_SCHEME_LENGTH = 32 -INTERNET_MAX_PATH_LENGTH = 2048 -INTERNET_MAX_URL_LENGTH = ( - INTERNET_MAX_SCHEME_LENGTH + len("://") + INTERNET_MAX_PATH_LENGTH) - -FOREGROUND_BLUE = 0x0001 -FOREGROUND_GREEN = 0x0002 -FOREGROUND_RED = 0x0004 -FOREGROUND_INTENSITY = 0x0008 - -BACKGROUND_BLUE = 0x0010 -BACKGROUND_GREEN = 0x0020 -BACKGROUND_RED = 0x0040 -BACKGROUND_INTENSITY = 0x0080 - -COMMON_LVB_REVERSE_VIDEO = 0x4000 -COMMON_LVB_UNDERSCORE = 0x8000 - -UrlCreateFromPathW = shlwapi.UrlCreateFromPathW -UrlCreateFromPathW.argtypes = [ - PCTSTR, PTSTR, ctypes.POINTER(DWORD), DWORD] -UrlCreateFromPathW.restype = ctypes.HRESULT - -SetEnvironmentVariableW = kernel32.SetEnvironmentVariableW -SetEnvironmentVariableW.argtypes = [LPCTSTR, LPCTSTR] -SetEnvironmentVariableW.restype = wintypes.BOOL - -GetEnvironmentVariableW = kernel32.GetEnvironmentVariableW -GetEnvironmentVariableW.argtypes = [LPCTSTR, LPTSTR, DWORD] -GetEnvironmentVariableW.restype = DWORD - -GetEnvironmentStringsW = kernel32.GetEnvironmentStringsW -GetEnvironmentStringsW.argtypes = [] -GetEnvironmentStringsW.restype = ctypes.c_void_p - -FreeEnvironmentStringsW = kernel32.FreeEnvironmentStringsW -FreeEnvironmentStringsW.argtypes = [ctypes.c_void_p] -FreeEnvironmentStringsW.restype = ctypes.c_bool - -GetStdHandle = kernel32.GetStdHandle -GetStdHandle.argtypes = [DWORD] -GetStdHandle.restype = HANDLE - - -class COORD(ctypes.Structure): - - _fields_ = [ - ("X", SHORT), - ("Y", SHORT), - ] - - -class SMALL_RECT(ctypes.Structure): - - _fields_ = [ - ("Left", SHORT), - ("Top", SHORT), - ("Right", SHORT), - ("Bottom", SHORT), - ] - - -class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): - - _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", WORD), - ("srWindow", SMALL_RECT), - ("dwMaximumWindowSize", COORD), - ] - - -GetConsoleScreenBufferInfo = kernel32.GetConsoleScreenBufferInfo -GetConsoleScreenBufferInfo.argtypes = [ - HANDLE, ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)] -GetConsoleScreenBufferInfo.restype = BOOL - -GetConsoleOutputCP = kernel32.GetConsoleOutputCP -GetConsoleOutputCP.argtypes = [] -GetConsoleOutputCP.restype = UINT - -SetConsoleOutputCP = kernel32.SetConsoleOutputCP -SetConsoleOutputCP.argtypes = [UINT] -SetConsoleOutputCP.restype = BOOL - -GetConsoleCP = kernel32.GetConsoleCP -GetConsoleCP.argtypes = [] -GetConsoleCP.restype = UINT - -SetConsoleCP = kernel32.SetConsoleCP -SetConsoleCP.argtypes = [UINT] -SetConsoleCP.restype = BOOL - -SetConsoleTextAttribute = kernel32.SetConsoleTextAttribute -SetConsoleTextAttribute.argtypes = [HANDLE, WORD] -SetConsoleTextAttribute.restype = BOOL - -SetConsoleCursorPosition = kernel32.SetConsoleCursorPosition -SetConsoleCursorPosition.argtypes = [HANDLE, COORD] -SetConsoleCursorPosition.restype = BOOL - -ReadConsoleW = kernel32.ReadConsoleW -ReadConsoleW.argtypes = [HANDLE, LPVOID, DWORD, ctypes.POINTER(DWORD), LPVOID] -ReadConsoleW.restype = BOOL - -MultiByteToWideChar = kernel32.MultiByteToWideChar -MultiByteToWideChar.argtypes = [ - UINT, DWORD, LPCSTR, ctypes.c_int, LPWSTR, ctypes.c_int] -MultiByteToWideChar.restype = ctypes.c_int - -WideCharToMultiByte = kernel32.WideCharToMultiByte -WideCharToMultiByte.argtypes = [ - UINT, DWORD, LPCWSTR, ctypes.c_int, LPSTR, ctypes.c_int, LPCSTR, LPBOOL] -WideCharToMultiByte.restpye = ctypes.c_int - -MoveFileW = kernel32.MoveFileW -MoveFileW.argtypes = [LPCTSTR, LPCTSTR] -MoveFileW.restype = BOOL - -if hasattr(kernel32, "GetFileInformationByHandleEx"): - GetFileInformationByHandleEx = kernel32.GetFileInformationByHandleEx - GetFileInformationByHandleEx.argtypes = [ - HANDLE, ctypes.c_int, ctypes.c_void_p, DWORD] - GetFileInformationByHandleEx.restype = BOOL -else: - # Windows XP - GetFileInformationByHandleEx = None - -MAX_PATH = 260 -FileNameInfo = 2 - - -class FILE_NAME_INFO(ctypes.Structure): - _fields_ = [ - ("FileNameLength", DWORD), - ("FileName", WCHAR), - ] - - -_get_osfhandle = msvcrt._get_osfhandle -_get_osfhandle.argtypes = [ctypes.c_int] -_get_osfhandle.restype = HANDLE - -GetFileType = kernel32.GetFileType -GetFileType.argtypes = [HANDLE] -GetFileType.restype = DWORD - -FILE_TYPE_PIPE = 0x0003 diff --git a/libs/common/mutagen/_tags.py b/libs/common/mutagen/_tags.py index c3f2ebf6..aa62a771 100644 --- a/libs/common/mutagen/_tags.py +++ b/libs/common/mutagen/_tags.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -115,7 +114,7 @@ class Metadata(Tags): raise NotImplementedError @loadfile(writable=False) - def save(self, filething, **kwargs): + def save(self, filething=None, **kwargs): """save(filething=None, **kwargs) Save changes to a file. @@ -129,7 +128,7 @@ class Metadata(Tags): raise NotImplementedError @loadfile(writable=False) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. diff --git a/libs/common/mutagen/_tools/__init__.py b/libs/common/mutagen/_tools/__init__.py index 3e6b1556..94b5bb2f 100644 --- a/libs/common/mutagen/_tools/__init__.py +++ b/libs/common/mutagen/_tools/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 Christoph Reiter # # This program is free software; you can redistribute it and/or modify diff --git a/libs/common/mutagen/_tools/_util.py b/libs/common/mutagen/_tools/_util.py index 4e050769..513f3989 100644 --- a/libs/common/mutagen/_tools/_util.py +++ b/libs/common/mutagen/_tools/_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2015 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -11,8 +10,7 @@ import signal import contextlib import optparse -from mutagen._senf import print_ -from mutagen._compat import text_type, iterbytes +from mutagen._util import iterbytes def split_escape(string, sep, maxsplit=None, escape_char="\\"): @@ -25,7 +23,7 @@ def split_escape(string, sep, maxsplit=None, escape_char="\\"): assert len(escape_char) == 1 if isinstance(string, bytes): - if isinstance(escape_char, text_type): + if isinstance(escape_char, str): escape_char = escape_char.encode("ascii") iter_ = iterbytes else: @@ -88,8 +86,4 @@ class SignalHandler(object): raise SystemExit("Aborted...") -class OptionParser(optparse.OptionParser): - """OptionParser subclass which supports printing Unicode under Windows""" - - def print_help(self, file=None): - print_(self.format_help(), file=file) +OptionParser = optparse.OptionParser diff --git a/libs/common/mutagen/_tools/mid3cp.py b/libs/common/mutagen/_tools/mid3cp.py index 1339548d..b48d285c 100644 --- a/libs/common/mutagen/_tools/mid3cp.py +++ b/libs/common/mutagen/_tools/mid3cp.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2014 Marcus Sundman # # This program is free software; you can redistribute it and/or modify @@ -15,8 +14,6 @@ import os.path import mutagen import mutagen.id3 -from mutagen._senf import print_, argv -from mutagen._compat import text_type from ._util import SignalHandler, OptionParser @@ -25,11 +22,6 @@ VERSION = (0, 1) _sig = SignalHandler() -def printerr(*args, **kwargs): - kwargs.setdefault("file", sys.stderr) - print_(*args, **kwargs) - - class ID3OptionParser(OptionParser): def __init__(self): mutagen_version = mutagen.version_string @@ -52,15 +44,15 @@ def copy(src, dst, merge, write_v1=True, excluded_tags=None, verbose=False): try: id3 = mutagen.id3.ID3(src, translate=False) except mutagen.id3.ID3NoHeaderError: - print_(u"No ID3 header found in ", src, file=sys.stderr) + print(u"No ID3 header found in ", src, file=sys.stderr) return 1 except Exception as err: - print_(str(err), file=sys.stderr) + print(str(err), file=sys.stderr) return 1 if verbose: - print_(u"File", src, u"contains:", file=sys.stderr) - print_(id3.pprint(), file=sys.stderr) + print(u"File", src, u"contains:", file=sys.stderr) + print(id3.pprint(), file=sys.stderr) for tag in excluded_tags: id3.delall(tag) @@ -72,7 +64,7 @@ def copy(src, dst, merge, write_v1=True, excluded_tags=None, verbose=False): # no need to merge pass except Exception as err: - print_(str(err), file=sys.stderr) + print(str(err), file=sys.stderr) return 1 else: for frame in id3.values(): @@ -91,12 +83,12 @@ def copy(src, dst, merge, write_v1=True, excluded_tags=None, verbose=False): try: id3.save(dst, v1=(2 if write_v1 else 0), v2_version=v2_version) except Exception as err: - print_(u"Error saving", dst, u":\n%s" % text_type(err), - file=sys.stderr) + print(u"Error saving", dst, u":\n%s" % str(err), + file=sys.stderr) return 1 else: if verbose: - print_(u"Successfully saved", dst, file=sys.stderr) + print(u"Successfully saved", dst, file=sys.stderr) return 0 @@ -120,12 +112,12 @@ def main(argv): (src, dst) = args if not os.path.isfile(src): - print_(u"File not found:", src, file=sys.stderr) + print(u"File not found:", src, file=sys.stderr) parser.print_help(file=sys.stderr) return 1 if not os.path.isfile(dst): - printerr(u"File not found:", dst, file=sys.stderr) + print(u"File not found:", dst, file=sys.stderr) parser.print_help(file=sys.stderr) return 1 @@ -139,4 +131,4 @@ def main(argv): def entry_point(): _sig.init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_tools/mid3iconv.py b/libs/common/mutagen/_tools/mid3iconv.py index 554f6bb8..501066a4 100644 --- a/libs/common/mutagen/_tools/mid3iconv.py +++ b/libs/common/mutagen/_tools/mid3iconv.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Emfox Zhou # # This program is free software; you can redistribute it and/or modify @@ -15,8 +14,6 @@ import locale import mutagen import mutagen.id3 -from mutagen._senf import argv, print_, fsnative -from mutagen._compat import text_type from ._util import SignalHandler, OptionParser @@ -75,7 +72,7 @@ def update(options, filenames): for filename in filenames: with _sig.block(): if verbose != "quiet": - print_(u"Updating", filename) + print(u"Updating", filename) if has_id3v1(filename) and not noupdate and force_v1: mutagen.id3.delete(filename, False, True) @@ -84,10 +81,10 @@ def update(options, filenames): id3 = mutagen.id3.ID3(filename) except mutagen.id3.ID3NoHeaderError: if verbose != "quiet": - print_(u"No ID3 header found; skipping...") + print(u"No ID3 header found; skipping...") continue except Exception as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) continue for tag in filter(lambda t: t.startswith(("T", "COMM")), id3): @@ -111,7 +108,7 @@ def update(options, filenames): frame.encoding = 1 if verbose == "debug": - print_(id3.pprint()) + print(id3.pprint()) if not noupdate: if remove_v1: @@ -154,9 +151,9 @@ def main(argv): for i, arg in enumerate(argv): if arg == "-v1": - argv[i] = fsnative(u"--force-v1") + argv[i] = "--force-v1" elif arg == "-removev1": - argv[i] = fsnative(u"--remove-v1") + argv[i] = "--remove-v1" (options, args) = parser.parse_args(argv[1:]) @@ -168,4 +165,4 @@ def main(argv): def entry_point(): _sig.init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_tools/mid3v2.py b/libs/common/mutagen/_tools/mid3v2.py index 2a79e3b8..b6949b76 100644 --- a/libs/common/mutagen/_tools/mid3v2.py +++ b/libs/common/mutagen/_tools/mid3v2.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2005 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -8,18 +7,17 @@ """Pretend to be /usr/bin/id3v2 from id3lib, sort of.""" +import os import sys import codecs import mimetypes +import warnings from optparse import SUPPRESS_HELP import mutagen import mutagen.id3 from mutagen.id3 import Encoding, PictureType -from mutagen._senf import fsnative, print_, argv, fsn2text, fsn2bytes, \ - bytes2fsn -from mutagen._compat import PY2, text_type from ._util import split_escape, SignalHandler, OptionParser @@ -57,7 +55,7 @@ Any editing operation will cause the ID3 tag to be upgraded to ID3v2.4. def list_frames(option, opt, value, parser): items = mutagen.id3.Frames.items() for name, frame in sorted(items): - print_(u" --%s %s" % (name, frame.__doc__.split("\n")[0])) + print(u" --%s %s" % (name, frame.__doc__.split("\n")[0])) raise SystemExit @@ -65,13 +63,13 @@ def list_frames_2_2(option, opt, value, parser): items = mutagen.id3.Frames_2_2.items() items.sort() for name, frame in items: - print_(u" --%s %s" % (name, frame.__doc__.split("\n")[0])) + print(u" --%s %s" % (name, frame.__doc__.split("\n")[0])) raise SystemExit def list_genres(option, opt, value, parser): for i, genre in enumerate(mutagen.id3.TCON.GENRES): - print_(u"%3d: %s" % (i, genre)) + print(u"%3d: %s" % (i, genre)) raise SystemExit @@ -79,7 +77,7 @@ def delete_tags(filenames, v1, v2): for filename in filenames: with _sig.block(): if verbose: - print_(u"deleting ID3 tag info in", filename, file=sys.stderr) + print(u"deleting ID3 tag info in", filename, file=sys.stderr) mutagen.id3.delete(filename, v1, v2) @@ -88,22 +86,22 @@ def delete_frames(deletes, filenames): try: deletes = frame_from_fsnative(deletes) except ValueError as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) frames = deletes.split(",") for filename in filenames: with _sig.block(): if verbose: - print_(u"deleting %s from" % deletes, filename, - file=sys.stderr) + print("deleting %s from" % deletes, filename, + file=sys.stderr) try: id3 = mutagen.id3.ID3(filename) except mutagen.id3.ID3NoHeaderError: if verbose: - print_(u"No ID3 header found; skipping.", file=sys.stderr) + print(u"No ID3 header found; skipping.", file=sys.stderr) except Exception as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) raise SystemExit(1) else: for frame in frames: @@ -116,36 +114,32 @@ def frame_from_fsnative(arg): or raises ValueError. """ - assert isinstance(arg, fsnative) - - text = fsn2text(arg, strict=True) - if PY2: - return text.encode("ascii") - else: - return text.encode("ascii").decode("ascii") + assert isinstance(arg, str) + return arg.encode("ascii").decode("ascii") def value_from_fsnative(arg, escape): - """Takes an item from argv and returns a text_type value without + """Takes an item from argv and returns a str value without surrogate escapes or raises ValueError. """ - assert isinstance(arg, fsnative) + assert isinstance(arg, str) if escape: - bytes_ = fsn2bytes(arg) - if PY2: - bytes_ = bytes_.decode("string_escape") - else: + bytes_ = os.fsencode(arg) + # With py3.7 this has started to warn for invalid escapes, but we + # don't control the input so ignore it. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") bytes_ = codecs.escape_decode(bytes_)[0] - arg = bytes2fsn(bytes_) + arg = os.fsdecode(bytes_) - text = fsn2text(arg, strict=True) + text = arg.encode("utf-8").decode("utf-8") return text def error(*args): - print_(*args, file=sys.stderr) + print(*args, file=sys.stderr) raise SystemExit(1) @@ -167,7 +161,7 @@ def write_files(edits, filenames, escape): try: frame = frame_from_fsnative(frame) except ValueError as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) assert isinstance(frame, str) @@ -177,9 +171,9 @@ def write_files(edits, filenames, escape): try: value = value_from_fsnative(value, escape) except ValueError as err: - error(u"%s: %s" % (frame, text_type(err))) + error(u"%s: %s" % (frame, str(err))) - assert isinstance(value, text_type) + assert isinstance(value, str) encoded_edits.append((frame, value)) edits = encoded_edits @@ -205,16 +199,16 @@ def write_files(edits, filenames, escape): for filename in filenames: with _sig.block(): if verbose: - print_(u"Writing", filename, file=sys.stderr) + print(u"Writing", filename, file=sys.stderr) try: id3 = mutagen.id3.ID3(filename) except mutagen.id3.ID3NoHeaderError: if verbose: - print_(u"No ID3 header found; creating a new tag", + print(u"No ID3 header found; creating a new tag", file=sys.stderr) id3 = mutagen.id3.ID3() except Exception as err: - print_(str(err), file=sys.stderr) + print(str(err), file=sys.stderr) continue for (frame, vlist) in edits.items(): if frame == "POPM": @@ -264,7 +258,7 @@ def write_files(edits, filenames, escape): with open(fn, "rb") as h: data = h.read() except IOError as e: - error(text_type(e)) + error(str(e)) frame = mutagen.id3.APIC(encoding=encoding, mime=mime, desc=desc, type=picture_type, data=data) @@ -338,31 +332,31 @@ def write_files(edits, filenames, escape): def list_tags(filenames): for filename in filenames: - print_("IDv2 tag info for", filename) + print("IDv2 tag info for", filename) try: id3 = mutagen.id3.ID3(filename, translate=False) except mutagen.id3.ID3NoHeaderError: - print_(u"No ID3 header found; skipping.") + print(u"No ID3 header found; skipping.") except Exception as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) raise SystemExit(1) else: - print_(id3.pprint()) + print(id3.pprint()) def list_tags_raw(filenames): for filename in filenames: - print_("Raw IDv2 tag info for", filename) + print("Raw IDv2 tag info for", filename) try: id3 = mutagen.id3.ID3(filename, translate=False) except mutagen.id3.ID3NoHeaderError: - print_(u"No ID3 header found; skipping.") + print(u"No ID3 header found; skipping.") except Exception as err: - print_(text_type(err), file=sys.stderr) + print(str(err), file=sys.stderr) raise SystemExit(1) else: for frame in id3.values(): - print_(text_type(repr(frame))) + print(str(repr(frame))) def main(argv): @@ -411,43 +405,43 @@ def main(argv): parser.add_option( "-a", "--artist", metavar='"ARTIST"', action="callback", help="Set the artist information", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TPE1"), + callback=lambda *args: args[3].edits.append(("--TPE1", args[2]))) parser.add_option( "-A", "--album", metavar='"ALBUM"', action="callback", help="Set the album title information", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TALB"), + callback=lambda *args: args[3].edits.append(("--TALB", args[2]))) parser.add_option( "-t", "--song", metavar='"SONG"', action="callback", help="Set the song title information", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TIT2"), + callback=lambda *args: args[3].edits.append(("--TIT2", args[2]))) parser.add_option( "-c", "--comment", metavar='"DESCRIPTION":"COMMENT":"LANGUAGE"', action="callback", help="Set the comment information", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--COMM"), + callback=lambda *args: args[3].edits.append(("--COMM", args[2]))) parser.add_option( "-p", "--picture", metavar='"FILENAME":"DESCRIPTION":"IMAGE-TYPE":"MIME-TYPE"', action="callback", help="Set the picture", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--APIC"), + callback=lambda *args: args[3].edits.append(("--APIC", args[2]))) parser.add_option( "-g", "--genre", metavar='"GENRE"', action="callback", help="Set the genre or genre number", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TCON"), + callback=lambda *args: args[3].edits.append(("--TCON", args[2]))) parser.add_option( "-y", "--year", "--date", metavar='YYYY[-MM-DD]', action="callback", help="Set the year/date", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TDRC"), + callback=lambda *args: args[3].edits.append(("--TDRC", args[2]))) parser.add_option( "-T", "--track", metavar='"num/num"', action="callback", help="Set the track number/(optional) total tracks", type="string", - callback=lambda *args: args[3].edits.append((fsnative(u"--TRCK"), + callback=lambda *args: args[3].edits.append(("--TRCK", args[2]))) for key, frame in mutagen.id3.Frames.items(): @@ -487,4 +481,4 @@ def main(argv): def entry_point(): _sig.init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_tools/moggsplit.py b/libs/common/mutagen/_tools/moggsplit.py index 710f0dfe..de382060 100644 --- a/libs/common/mutagen/_tools/moggsplit.py +++ b/libs/common/mutagen/_tools/moggsplit.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -9,9 +8,9 @@ """Split a multiplex/chained Ogg file into its component parts.""" import os +import sys import mutagen.ogg -from mutagen._senf import argv from ._util import SignalHandler, OptionParser @@ -72,4 +71,4 @@ def main(argv): def entry_point(): _sig.init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_tools/mutagen_inspect.py b/libs/common/mutagen/_tools/mutagen_inspect.py index 6bd6c614..fac529a9 100644 --- a/libs/common/mutagen/_tools/mutagen_inspect.py +++ b/libs/common/mutagen/_tools/mutagen_inspect.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2005 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -8,8 +7,7 @@ """Full tag list for any given file.""" -from mutagen._senf import print_, argv -from mutagen._compat import text_type +import sys from ._util import SignalHandler, OptionParser @@ -20,7 +18,7 @@ _sig = SignalHandler() def main(argv): from mutagen import File - parser = OptionParser() + parser = OptionParser(usage="usage: %prog [options] FILE [FILE...]") parser.add_option("--no-flac", help="Compatibility; does nothing.") parser.add_option("--no-mp3", help="Compatibility; does nothing.") parser.add_option("--no-apev2", help="Compatibility; does nothing.") @@ -30,16 +28,16 @@ def main(argv): raise SystemExit(parser.print_help() or 1) for filename in args: - print_(u"--", filename) + print(u"--", filename) try: - print_(u"-", File(filename).pprint()) + print(u"-", File(filename).pprint()) except AttributeError: - print_(u"- Unknown file type") + print(u"- Unknown file type") except Exception as err: - print_(text_type(err)) - print_(u"") + print(str(err)) + print(u"") def entry_point(): _sig.init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_tools/mutagen_pony.py b/libs/common/mutagen/_tools/mutagen_pony.py index e4a496c7..69b29a67 100644 --- a/libs/common/mutagen/_tools/mutagen_pony.py +++ b/libs/common/mutagen/_tools/mutagen_pony.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2005 Joe Wreschnig, Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -10,8 +9,6 @@ import os import sys import traceback -from mutagen._senf import print_, argv - from ._util import SignalHandler @@ -83,7 +80,7 @@ def check_dir(path): from mutagen.mp3 import MP3 rep = Report(path) - print_(u"Scanning", path) + print(u"Scanning", path) for path, dirs, files in os.walk(path): files.sort() for fn in files: @@ -100,12 +97,12 @@ def check_dir(path): else: rep.success(mp3.tags) - print_(str(rep)) + print(str(rep)) def main(argv): if len(argv) == 1: - print_(u"Usage:", argv[0], u"directory ...") + print(u"Usage:", argv[0], u"directory ...") else: for path in argv[1:]: check_dir(path) @@ -113,4 +110,4 @@ def main(argv): def entry_point(): SignalHandler().init() - return main(argv) + return main(sys.argv) diff --git a/libs/common/mutagen/_util.py b/libs/common/mutagen/_util.py index 1332f9d3..8975380e 100644 --- a/libs/common/mutagen/_util.py +++ b/libs/common/mutagen/_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -19,20 +18,36 @@ import errno import decimal from io import BytesIO -try: - import mmap -except ImportError: - # Google App Engine has no mmap: - # https://github.com/quodlibet/mutagen/issues/286 - mmap = None - from collections import namedtuple from contextlib import contextmanager from functools import wraps from fnmatch import fnmatchcase -from ._compat import chr_, PY2, iteritems, iterbytes, integer_types, xrange, \ - izip, text_type, reraise + +_DEFAULT_BUFFER_SIZE = 2 ** 20 + + +def endswith(text, end): + # usefull for paths which can be both, str and bytes + if isinstance(text, str): + if not isinstance(end, str): + end = end.decode("ascii") + else: + if not isinstance(end, bytes): + end = end.encode("ascii") + return text.endswith(end) + + +def reraise(tp, value, tb): + raise tp(value).with_traceback(tb) + + +def bchr(x): + return bytes([x]) + + +def iterbytes(b): + return (bytes([v]) for v in b) def intround(value): @@ -50,7 +65,7 @@ def is_fileobj(fileobj): file object """ - return not (isinstance(fileobj, (text_type, bytes)) or + return not (isinstance(fileobj, (str, bytes)) or hasattr(fileobj, "__fspath__")) @@ -105,8 +120,8 @@ def fileobj_name(fileobj): """ value = getattr(fileobj, "name", u"") - if not isinstance(value, (text_type, bytes)): - value = text_type(value) + if not isinstance(value, (str, bytes)): + value = str(value) return value @@ -212,7 +227,7 @@ def _openfile(instance, filething, filename, fileobj, writable, create): fileobj = filething elif hasattr(filething, "__fspath__"): filename = filething.__fspath__() - if not isinstance(filename, (bytes, text_type)): + if not isinstance(filename, (bytes, str)): raise TypeError("expected __fspath__() to return a filename") else: filename = filething @@ -302,9 +317,6 @@ def hashable(cls): Needs a working __eq__ and __hash__ and will add a __ne__. """ - # py2 - assert "__hash__" in cls.__dict__ - # py3 assert cls.__dict__["__hash__"] is not None assert "__eq__" in cls.__dict__ @@ -340,8 +352,8 @@ def enum(cls): new_type.__module__ = cls.__module__ map_ = {} - for key, value in iteritems(d): - if key.upper() == key and isinstance(value, integer_types): + for key, value in d.items(): + if key.upper() == key and isinstance(value, int): value_instance = new_type(value) setattr(new_type, key, value_instance) map_[value] = key @@ -389,8 +401,8 @@ def flags(cls): new_type.__module__ = cls.__module__ map_ = {} - for key, value in iteritems(d): - if key.upper() == key and isinstance(value, integer_types): + for key, value in d.items(): + if key.upper() == key and isinstance(value, int): value_instance = new_type(value) setattr(new_type, key, value_instance) map_[value] = key @@ -403,7 +415,7 @@ def flags(cls): matches.append("%s.%s" % (type(self).__name__, v)) value &= ~k if value != 0 or not matches: - matches.append(text_type(value)) + matches.append(str(value)) return " | ".join(matches) @@ -443,25 +455,13 @@ class DictMixin(object): else: return True - if PY2: - has_key = __has_key - __contains__ = __has_key - if PY2: - iterkeys = lambda self: iter(self.keys()) - def values(self): return [self[k] for k in self.keys()] - if PY2: - itervalues = lambda self: iter(self.values()) - def items(self): - return list(izip(self.keys(), self.values())) - - if PY2: - iteritems = lambda s: iter(s.items()) + return list(zip(self.keys(), self.values())) def clear(self): for key in list(self.keys()): @@ -591,7 +591,7 @@ def _fill_cdata(cls): funcs["to_%s%s%s" % (prefix, name, esuffix)] = pack funcs["to_%sint%s%s" % (prefix, bits, esuffix)] = pack - for key, func in iteritems(funcs): + for key, func in funcs.items(): setattr(cls, key, staticmethod(func)) @@ -602,12 +602,11 @@ class cdata(object): uint32_le(data)/to_uint32_le(num)/uint32_le_from(data, offset=0) """ - from struct import error - error = error + error = struct.error bitswap = b''.join( - chr_(sum(((val >> i) & 1) << (7 - i) for i in xrange(8))) - for val in xrange(256)) + bchr(sum(((val >> i) & 1) << (7 - i) for i in range(8))) + for val in range(256)) test_bit = staticmethod(lambda value, n: bool((value >> n) & 1)) @@ -683,65 +682,7 @@ def seek_end(fileobj, offset): fileobj.seek(-offset, 2) -def mmap_move(fileobj, dest, src, count): - """Mmaps the file object if possible and moves 'count' data - from 'src' to 'dest'. All data has to be inside the file size - (enlarging the file through this function isn't possible) - - Will adjust the file offset. - - Args: - fileobj (fileobj) - dest (int): The destination offset - src (int): The source offset - count (int) The amount of data to move - Raises: - mmap.error: In case move failed - IOError: In case an operation on the fileobj fails - ValueError: In case invalid parameters were given - """ - - assert mmap is not None, "no mmap support" - - if dest < 0 or src < 0 or count < 0: - raise ValueError("Invalid parameters") - - try: - fileno = fileobj.fileno() - except (AttributeError, IOError): - raise mmap.error( - "File object does not expose/support a file descriptor") - - fileobj.seek(0, 2) - filesize = fileobj.tell() - length = max(dest, src) + count - - if length > filesize: - raise ValueError("Not in file size boundary") - - offset = ((min(dest, src) // mmap.ALLOCATIONGRANULARITY) * - mmap.ALLOCATIONGRANULARITY) - assert dest >= offset - assert src >= offset - assert offset % mmap.ALLOCATIONGRANULARITY == 0 - - # Windows doesn't handle empty mappings, add a fast path here instead - if count == 0: - return - - # fast path - if src == dest: - return - - fileobj.flush() - file_map = mmap.mmap(fileno, length - offset, offset=offset) - try: - file_map.move(dest - offset, src - offset, count) - finally: - file_map.close() - - -def resize_file(fobj, diff, BUFFER_SIZE=2 ** 16): +def resize_file(fobj, diff, BUFFER_SIZE=_DEFAULT_BUFFER_SIZE): """Resize a file by `diff`. New space will be filled with zeros. @@ -778,7 +719,7 @@ def resize_file(fobj, diff, BUFFER_SIZE=2 ** 16): raise -def fallback_move(fobj, dest, src, count, BUFFER_SIZE=2 ** 16): +def move_bytes(fobj, dest, src, count, BUFFER_SIZE=_DEFAULT_BUFFER_SIZE): """Moves data around using read()/write(). Args: @@ -821,12 +762,11 @@ def fallback_move(fobj, dest, src, count, BUFFER_SIZE=2 ** 16): fobj.flush() -def insert_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16): +def insert_bytes(fobj, size, offset, BUFFER_SIZE=_DEFAULT_BUFFER_SIZE): """Insert size bytes of empty space starting at offset. fobj must be an open file object, open rb+ or - equivalent. Mutagen tries to use mmap to resize the file, but - falls back to a significantly slower method if mmap fails. + equivalent. Args: fobj (fileobj) @@ -847,22 +787,14 @@ def insert_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16): raise ValueError resize_file(fobj, size, BUFFER_SIZE) - - if mmap is not None: - try: - mmap_move(fobj, offset + size, offset, movesize) - except mmap.error: - fallback_move(fobj, offset + size, offset, movesize, BUFFER_SIZE) - else: - fallback_move(fobj, offset + size, offset, movesize, BUFFER_SIZE) + move_bytes(fobj, offset + size, offset, movesize, BUFFER_SIZE) -def delete_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16): +def delete_bytes(fobj, size, offset, BUFFER_SIZE=_DEFAULT_BUFFER_SIZE): """Delete size bytes of empty space starting at offset. fobj must be an open file object, open rb+ or - equivalent. Mutagen tries to use mmap to resize the file, but - falls back to a significantly slower method if mmap fails. + equivalent. Args: fobj (fileobj) @@ -882,14 +814,7 @@ def delete_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16): if movesize < 0: raise ValueError - if mmap is not None: - try: - mmap_move(fobj, offset, offset + size, movesize) - except mmap.error: - fallback_move(fobj, offset, offset + size, movesize, BUFFER_SIZE) - else: - fallback_move(fobj, offset, offset + size, movesize, BUFFER_SIZE) - + move_bytes(fobj, offset, offset + size, movesize, BUFFER_SIZE) resize_file(fobj, -size, BUFFER_SIZE) @@ -933,7 +858,7 @@ def dict_match(d, key, default=None): if key in d and "[" not in key: return d[key] else: - for pattern, value in iteritems(d): + for pattern, value in d.items(): if fnmatchcase(key, pattern): return value return default @@ -1075,7 +1000,7 @@ class BitReader(object): raise BitReaderError("not enough data") return data - return bytes(bytearray(self.bits(8) for _ in xrange(count))) + return bytes(bytearray(self.bits(8) for _ in range(count))) def skip(self, count): """Skip `count` bits. diff --git a/libs/common/mutagen/_vorbis.py b/libs/common/mutagen/_vorbis.py index f8b0ee7a..3fd4718c 100644 --- a/libs/common/mutagen/_vorbis.py +++ b/libs/common/mutagen/_vorbis.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005-2006 Joe Wreschnig # 2013 Christoph Reiter # @@ -17,10 +16,10 @@ The specification is at http://www.xiph.org/vorbis/doc/v-comment.html. """ import sys +from io import BytesIO import mutagen -from ._compat import reraise, BytesIO, text_type, xrange, PY3, PY2 -from mutagen._util import DictMixin, cdata, MutagenError +from mutagen._util import DictMixin, cdata, MutagenError, reraise def is_valid_key(key): @@ -32,7 +31,7 @@ def is_valid_key(key): Takes str/unicode in Python 2, unicode in Python 3 """ - if PY3 and isinstance(key, bytes): + if isinstance(key, bytes): raise TypeError("needs to be str not bytes") for c in key: @@ -104,7 +103,7 @@ class VComment(mutagen.Tags, list): vendor_length = cdata.uint_le(fileobj.read(4)) self.vendor = fileobj.read(vendor_length).decode('utf-8', errors) count = cdata.uint_le(fileobj.read(4)) - for i in xrange(count): + for i in range(count): length = cdata.uint_le(fileobj.read(4)) try: string = fileobj.read(length).decode('utf-8', errors) @@ -124,9 +123,7 @@ class VComment(mutagen.Tags, list): except UnicodeEncodeError: raise VorbisEncodingError("invalid tag name %r" % tag) else: - # string keys in py3k - if PY3: - tag = tag.decode("ascii") + tag = tag.decode("ascii") if is_valid_key(tag): self.append((tag, value)) @@ -145,30 +142,19 @@ class VComment(mutagen.Tags, list): In Python 3 all keys and values have to be a string. """ - if not isinstance(self.vendor, text_type): - if PY3: - raise ValueError("vendor needs to be str") - - try: - self.vendor.decode('utf-8') - except UnicodeDecodeError: - raise ValueError + if not isinstance(self.vendor, str): + raise ValueError("vendor needs to be str") for key, value in self: try: if not is_valid_key(key): - raise ValueError + raise ValueError("%r is not a valid key" % key) except TypeError: raise ValueError("%r is not a valid key" % key) - if not isinstance(value, text_type): - if PY3: - raise ValueError("%r needs to be str" % key) - - try: - value.decode("utf-8") - except Exception: - raise ValueError("%r is not a valid value" % value) + if not isinstance(value, str): + err = "%r needs to be str for key %r" % (value, key) + raise ValueError(err) return True @@ -213,7 +199,7 @@ class VComment(mutagen.Tags, list): def pprint(self): def _decode(value): - if not isinstance(value, text_type): + if not isinstance(value, str): return value.decode('utf-8', 'replace') return value @@ -221,7 +207,7 @@ class VComment(mutagen.Tags, list): return u"\n".join(tags) -class VCommentDict(VComment, DictMixin): +class VCommentDict(VComment, DictMixin): # type: ignore """A VComment that looks like a dictionary. This object differs from a dictionary in two ways. First, @@ -242,7 +228,6 @@ class VCommentDict(VComment, DictMixin): work. """ - # PY3 only if isinstance(key, slice): return VComment.__getitem__(self, key) @@ -260,7 +245,6 @@ class VCommentDict(VComment, DictMixin): def __delitem__(self, key): """Delete all values associated with the key.""" - # PY3 only if isinstance(key, slice): return VComment.__delitem__(self, key) @@ -296,7 +280,6 @@ class VCommentDict(VComment, DictMixin): string. """ - # PY3 only if isinstance(key, slice): return VComment.__setitem__(self, key, values) @@ -310,9 +293,6 @@ class VCommentDict(VComment, DictMixin): except KeyError: pass - if PY2: - key = key.encode('ascii') - for value in values: self.append((key, value)) diff --git a/libs/common/mutagen/aac.py b/libs/common/mutagen/aac.py index fa6f7064..274b20df 100644 --- a/libs/common/mutagen/aac.py +++ b/libs/common/mutagen/aac.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2014 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -15,9 +14,8 @@ from mutagen import StreamInfo from mutagen._file import FileType from mutagen._util import BitReader, BitReaderError, MutagenError, loadfile, \ - convert_error + convert_error, endswith from mutagen.id3._util import BitPaddedInt -from mutagen._compat import endswith, xrange _FREQS = [ @@ -243,7 +241,7 @@ class ProgramConfigElement(object): elms = num_front_channel_elements + num_side_channel_elements + \ num_back_channel_elements channels = 0 - for i in xrange(elms): + for i in range(elms): channels += 1 element_is_cpe = r.bits(1) if element_is_cpe: @@ -323,7 +321,7 @@ class AACInfo(StreamInfo): self.channels = pce.channels # other pces.. - for i in xrange(npce): + for i in range(npce): ProgramConfigElement(r) r.align() except BitReaderError as e: @@ -347,7 +345,7 @@ class AACInfo(StreamInfo): # Try up to X times to find a sync word and read up to Y frames. # If more than Z frames are valid we assume a valid stream offset = start_offset - for i in xrange(max_sync_tries): + for i in range(max_sync_tries): fileobj.seek(offset) s = _ADTSStream.find_stream(fileobj, max_initial_read) if s is None: @@ -355,7 +353,7 @@ class AACInfo(StreamInfo): # start right after the last found offset offset += s.offset + 1 - for i in xrange(frames_max): + for i in range(frames_max): if not s.parse_frame(): break if not s.sync(max_resync_read): @@ -375,7 +373,10 @@ class AACInfo(StreamInfo): fileobj.seek(0, 2) stream_size = fileobj.tell() - (offset + s.offset) # approx - self.length = float(s.samples * stream_size) / (s.size * s.frequency) + self.length = 0.0 + if s.frequency != 0: + self.length = \ + float(s.samples * stream_size) / (s.size * s.frequency) def pprint(self): return u"AAC (%s), %d Hz, %.2f seconds, %d channel(s), %d bps" % ( diff --git a/libs/common/mutagen/ac3.py b/libs/common/mutagen/ac3.py new file mode 100644 index 00000000..34558734 --- /dev/null +++ b/libs/common/mutagen/ac3.py @@ -0,0 +1,329 @@ +# Copyright (C) 2019 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + + +"""Pure AC3 file information. +""" + +__all__ = ["AC3", "Open"] + +from mutagen import StreamInfo +from mutagen._file import FileType +from mutagen._util import ( + BitReader, + BitReaderError, + MutagenError, + convert_error, + enum, + loadfile, + endswith, +) + + +@enum +class ChannelMode(object): + DUALMONO = 0 + MONO = 1 + STEREO = 2 + C3F = 3 + C2F1R = 4 + C3F1R = 5 + C2F2R = 6 + C3F2R = 7 + + +AC3_CHANNELS = { + ChannelMode.DUALMONO: 2, + ChannelMode.MONO: 1, + ChannelMode.STEREO: 2, + ChannelMode.C3F: 3, + ChannelMode.C2F1R: 3, + ChannelMode.C3F1R: 4, + ChannelMode.C2F2R: 4, + ChannelMode.C3F2R: 5 +} + +AC3_HEADER_SIZE = 7 + +AC3_SAMPLE_RATES = [48000, 44100, 32000] + +AC3_BITRATES = [ + 32, 40, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 +] + + +@enum +class EAC3FrameType(object): + INDEPENDENT = 0 + DEPENDENT = 1 + AC3_CONVERT = 2 + RESERVED = 3 + + +EAC3_BLOCKS = [1, 2, 3, 6] + + +class AC3Error(MutagenError): + pass + + +class AC3Info(StreamInfo): + + """AC3 stream information. + The length of the stream is just a guess and might not be correct. + + Attributes: + channels (`int`): number of audio channels + length (`float`): file length in seconds, as a float + sample_rate (`int`): audio sampling rate in Hz + bitrate (`int`): audio bitrate, in bits per second + codec (`str`): ac-3 or ec-3 (Enhanced AC-3) + """ + + channels = 0 + length = 0 + sample_rate = 0 + bitrate = 0 + codec = 'ac-3' + + @convert_error(IOError, AC3Error) + def __init__(self, fileobj): + """Raises AC3Error""" + header = bytearray(fileobj.read(6)) + + if len(header) < 6: + raise AC3Error("not enough data") + + if not header.startswith(b"\x0b\x77"): + raise AC3Error("not a AC3 file") + + bitstream_id = header[5] >> 3 + if bitstream_id > 16: + raise AC3Error("invalid bitstream_id %i" % bitstream_id) + + fileobj.seek(2) + self._read_header(fileobj, bitstream_id) + + def _read_header(self, fileobj, bitstream_id): + bitreader = BitReader(fileobj) + try: + # This is partially based on code from + # https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/ac3_parser.c + if bitstream_id <= 10: # Normal AC-3 + self._read_header_normal(bitreader, bitstream_id) + else: # Enhanced AC-3 + self._read_header_enhanced(bitreader) + except BitReaderError as e: + raise AC3Error(e) + + self.length = self._guess_length(fileobj) + + def _read_header_normal(self, bitreader, bitstream_id): + r = bitreader + r.skip(16) # 16 bit CRC + sr_code = r.bits(2) + if sr_code == 3: + raise AC3Error("invalid sample rate code %i" % sr_code) + + frame_size_code = r.bits(6) + if frame_size_code > 37: + raise AC3Error("invalid frame size code %i" % frame_size_code) + + r.skip(5) # bitstream ID, already read + r.skip(3) # bitstream mode, not needed + channel_mode = ChannelMode(r.bits(3)) + r.skip(2) # dolby surround mode or surround mix level + lfe_on = r.bits(1) + + sr_shift = max(bitstream_id, 8) - 8 + try: + self.sample_rate = AC3_SAMPLE_RATES[sr_code] >> sr_shift + self.bitrate = (AC3_BITRATES[frame_size_code >> 1] * 1000 + ) >> sr_shift + except KeyError as e: + raise AC3Error(e) + self.channels = self._get_channels(channel_mode, lfe_on) + self._skip_unused_header_bits_normal(r, channel_mode) + + def _read_header_enhanced(self, bitreader): + r = bitreader + self.codec = "ec-3" + frame_type = r.bits(2) + if frame_type == EAC3FrameType.RESERVED: + raise AC3Error("invalid frame type %i" % frame_type) + + r.skip(3) # substream ID, not needed + + frame_size = (r.bits(11) + 1) << 1 + if frame_size < AC3_HEADER_SIZE: + raise AC3Error("invalid frame size %i" % frame_size) + + sr_code = r.bits(2) + try: + if sr_code == 3: + sr_code2 = r.bits(2) + if sr_code2 == 3: + raise AC3Error("invalid sample rate code %i" % sr_code2) + + numblocks_code = 3 + self.sample_rate = AC3_SAMPLE_RATES[sr_code2] // 2 + else: + numblocks_code = r.bits(2) + self.sample_rate = AC3_SAMPLE_RATES[sr_code] + + channel_mode = ChannelMode(r.bits(3)) + lfe_on = r.bits(1) + self.bitrate = 8 * frame_size * self.sample_rate // ( + EAC3_BLOCKS[numblocks_code] * 256) + except KeyError as e: + raise AC3Error(e) + r.skip(5) # bitstream ID, already read + self.channels = self._get_channels(channel_mode, lfe_on) + self._skip_unused_header_bits_enhanced( + r, frame_type, channel_mode, sr_code, numblocks_code) + + @staticmethod + def _skip_unused_header_bits_normal(bitreader, channel_mode): + r = bitreader + r.skip(5) # Dialogue Normalization + if r.bits(1): # Compression Gain Word Exists + r.skip(8) # Compression Gain Word + if r.bits(1): # Language Code Exists + r.skip(8) # Language Code + if r.bits(1): # Audio Production Information Exists + # Mixing Level, 5 Bits + # Room Type, 2 Bits + r.skip(7) + if channel_mode == ChannelMode.DUALMONO: + r.skip(5) # Dialogue Normalization, ch2 + if r.bits(1): # Compression Gain Word Exists, ch2 + r.skip(8) # Compression Gain Word, ch2 + if r.bits(1): # Language Code Exists, ch2 + r.skip(8) # Language Code, ch2 + if r.bits(1): # Audio Production Information Exists, ch2 + # Mixing Level, ch2, 5 Bits + # Room Type, ch2, 2 Bits + r.skip(7) + # Copyright Bit, 1 Bit + # Original Bit Stream, 1 Bit + r.skip(2) + timecod1e = r.bits(1) # Time Code First Halve Exists + timecod2e = r.bits(1) # Time Code Second Halve Exists + if timecod1e: + r.skip(14) # Time Code First Half + if timecod2e: + r.skip(14) # Time Code Second Half + if r.bits(1): # Additional Bit Stream Information Exists + addbsil = r.bit(6) # Additional Bit Stream Information Length + r.skip((addbsil + 1) * 8) + + @staticmethod + def _skip_unused_header_bits_enhanced(bitreader, frame_type, channel_mode, + sr_code, numblocks_code): + r = bitreader + r.skip(5) # Dialogue Normalization + if r.bits(1): # Compression Gain Word Exists + r.skip(8) # Compression Gain Word + if channel_mode == ChannelMode.DUALMONO: + r.skip(5) # Dialogue Normalization, ch2 + if r.bits(1): # Compression Gain Word Exists, ch2 + r.skip(8) # Compression Gain Word, ch2 + if frame_type == EAC3FrameType.DEPENDENT: + if r.bits(1): # chanmap exists + r.skip(16) # chanmap + if r.bits(1): # mixmdate, 1 Bit + # FIXME: Handle channel dependent fields + return + if r.bits(1): # Informational Metadata Exists + # bsmod, 3 Bits + # Copyright Bit, 1 Bit + # Original Bit Stream, 1 Bit + r.skip(5) + if channel_mode == ChannelMode.STEREO: + # dsurmod. 2 Bits + # dheadphonmod, 2 Bits + r.skip(4) + elif channel_mode >= ChannelMode.C2F2R: + r.skip(2) # dsurexmod + if r.bits(1): # Audio Production Information Exists + # Mixing Level, 5 Bits + # Room Type, 2 Bits + # adconvtyp, 1 Bit + r.skip(8) + if channel_mode == ChannelMode.DUALMONO: + if r.bits(1): # Audio Production Information Exists, ch2 + # Mixing Level, ch2, 5 Bits + # Room Type, ch2, 2 Bits + # adconvtyp, ch2, 1 Bit + r.skip(8) + if sr_code < 3: # if not half sample rate + r.skip(1) # sourcefscod + if frame_type == EAC3FrameType.INDEPENDENT and numblocks_code == 3: + r.skip(1) # convsync + if frame_type == EAC3FrameType.AC3_CONVERT: + if numblocks_code != 3: + if r.bits(1): # blkid + r.skip(6) # frmsizecod + if r.bits(1): # Additional Bit Stream Information Exists + addbsil = r.bit(6) # Additional Bit Stream Information Length + r.skip((addbsil + 1) * 8) + + @staticmethod + def _get_channels(channel_mode, lfe_on): + try: + return AC3_CHANNELS[channel_mode] + lfe_on + except KeyError as e: + raise AC3Error(e) + + def _guess_length(self, fileobj): + # use bitrate + data size to guess length + if self.bitrate == 0: + return + start = fileobj.tell() + fileobj.seek(0, 2) + length = fileobj.tell() - start + return 8.0 * length / self.bitrate + + def pprint(self): + return u"%s, %d Hz, %.2f seconds, %d channel(s), %d bps" % ( + self.codec, self.sample_rate, self.length, self.channels, + self.bitrate) + + +class AC3(FileType): + """AC3(filething) + + Arguments: + filething (filething) + + Load AC3 or EAC3 files. + + Tagging is not supported. + Use the ID3/APEv2 classes directly instead. + + Attributes: + info (`AC3Info`) + """ + + _mimes = ["audio/ac3"] + + @loadfile() + def load(self, filething): + self.info = AC3Info(filething.fileobj) + + def add_tags(self): + raise AC3Error("doesn't support tags") + + @staticmethod + def score(filename, fileobj, header): + return header.startswith(b"\x0b\x77") * 2 \ + + (endswith(filename, ".ac3") or endswith(filename, ".eac3")) + + +Open = AC3 +error = AC3Error diff --git a/libs/common/mutagen/aiff.py b/libs/common/mutagen/aiff.py index 66ec3af0..74d2f03d 100644 --- a/libs/common/mutagen/aiff.py +++ b/libs/common/mutagen/aiff.py @@ -1,6 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2014 Evan Purkhiser # 2014 Ben Ockmore +# 2019-2020 Philipp Wolfer # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -9,26 +9,30 @@ """AIFF audio stream information and tags.""" -import sys import struct from struct import pack -from ._compat import endswith, text_type, reraise from mutagen import StreamInfo, FileType -from mutagen.id3 import ID3 from mutagen.id3._util import ID3NoHeaderError, error as ID3Error -from mutagen._util import resize_bytes, delete_bytes, MutagenError, loadfile, \ - convert_error +from mutagen._iff import ( + IffChunk, + IffContainerChunkMixin, + IffFile, + IffID3, + InvalidChunk, + error as IffError, +) +from mutagen._util import ( + convert_error, + loadfile, + endswith, +) __all__ = ["AIFF", "Open", "delete"] -class error(MutagenError): - pass - - -class InvalidChunk(error): +class error(IffError): pass @@ -36,22 +40,10 @@ class InvalidChunk(error): _HUGE_VAL = 1.79769313486231e+308 -def is_valid_chunk_id(id): - assert isinstance(id, text_type) +def read_float(data): + """Raises OverflowError""" - return ((len(id) <= 4) and (min(id) >= u' ') and - (max(id) <= u'~')) - - -def assert_valid_chunk_id(id): - - assert isinstance(id, text_type) - - if not is_valid_chunk_id(id): - raise ValueError("AIFF key must be four ASCII characters.") - - -def read_float(data): # 10 bytes + assert len(data) == 10 expon, himant, lomant = struct.unpack('>hLL', data) sign = 1 if expon < 0: @@ -60,156 +52,70 @@ def read_float(data): # 10 bytes if expon == himant == lomant == 0: f = 0.0 elif expon == 0x7FFF: - f = _HUGE_VAL + raise OverflowError("inf and nan not supported") else: expon = expon - 16383 + # this can raise OverflowError too f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63) return sign * f -class IFFChunk(object): +class AIFFChunk(IffChunk): """Representation of a single IFF chunk""" - # Chunk headers are 8 bytes long (4 for ID and 4 for the size) - HEADER_SIZE = 8 + @classmethod + def parse_header(cls, header): + return struct.unpack('>4sI', header) - def __init__(self, fileobj, parent_chunk=None): - self.__fileobj = fileobj - self.parent_chunk = parent_chunk - self.offset = fileobj.tell() + @classmethod + def get_class(cls, id): + if id == 'FORM': + return AIFFFormChunk + else: + return cls - header = fileobj.read(self.HEADER_SIZE) - if len(header) < self.HEADER_SIZE: - raise InvalidChunk() + def write_new_header(self, id_, size): + self._fileobj.write(pack('>4sI', id_, size)) - self.id, self.data_size = struct.unpack('>4si', header) - - try: - self.id = self.id.decode('ascii') - except UnicodeDecodeError: - raise InvalidChunk() - - if not is_valid_chunk_id(self.id): - raise InvalidChunk() - - self.size = self.HEADER_SIZE + self.data_size - self.data_offset = fileobj.tell() - - def read(self): - """Read the chunks data""" - - self.__fileobj.seek(self.data_offset) - return self.__fileobj.read(self.data_size) - - def write(self, data): - """Write the chunk data""" - - if len(data) > self.data_size: - raise ValueError - - self.__fileobj.seek(self.data_offset) - self.__fileobj.write(data) - - def delete(self): - """Removes the chunk from the file""" - - delete_bytes(self.__fileobj, self.size, self.offset) - if self.parent_chunk is not None: - self.parent_chunk._update_size( - self.parent_chunk.data_size - self.size) - - def _update_size(self, data_size): - """Update the size of the chunk""" - - self.__fileobj.seek(self.offset + 4) - self.__fileobj.write(pack('>I', data_size)) - if self.parent_chunk is not None: - size_diff = self.data_size - data_size - self.parent_chunk._update_size( - self.parent_chunk.data_size - size_diff) - self.data_size = data_size - self.size = data_size + self.HEADER_SIZE - - def resize(self, new_data_size): - """Resize the file and update the chunk sizes""" - - resize_bytes( - self.__fileobj, self.data_size, new_data_size, self.data_offset) - self._update_size(new_data_size) + def write_size(self): + self._fileobj.write(pack('>I', self.data_size)) -class IFFFile(object): - """Representation of a IFF file""" +class AIFFFormChunk(AIFFChunk, IffContainerChunkMixin): + """The AIFF root chunk.""" + + def parse_next_subchunk(self): + return AIFFChunk.parse(self._fileobj, self) + + def __init__(self, fileobj, id, data_size, parent_chunk): + if id != u'FORM': + raise InvalidChunk('Expected FORM chunk, got %s' % id) + + AIFFChunk.__init__(self, fileobj, id, data_size, parent_chunk) + self.init_container() + + +class AIFFFile(IffFile): + """Representation of a AIFF file""" def __init__(self, fileobj): - self.__fileobj = fileobj - self.__chunks = {} - # AIFF Files always start with the FORM chunk which contains a 4 byte # ID before the start of other chunks - fileobj.seek(0) - self.__chunks[u'FORM'] = IFFChunk(fileobj) + super().__init__(AIFFChunk, fileobj) - # Skip past the 4 byte FORM id - fileobj.seek(IFFChunk.HEADER_SIZE + 4) - - # Where the next chunk can be located. We need to keep track of this - # since the size indicated in the FORM header may not match up with the - # offset determined from the size of the last chunk in the file - self.__next_offset = fileobj.tell() - - # Load all of the chunks - while True: - try: - chunk = IFFChunk(fileobj, self[u'FORM']) - except InvalidChunk: - break - self.__chunks[chunk.id.strip()] = chunk - - # Calculate the location of the next chunk, - # considering the pad byte - self.__next_offset = chunk.offset + chunk.size - self.__next_offset += self.__next_offset % 2 - fileobj.seek(self.__next_offset) + if self.root.id != u'FORM': + raise InvalidChunk("Root chunk must be a FORM chunk, got %s" + % self.root.id) def __contains__(self, id_): - """Check if the IFF file contains a specific chunk""" - - assert_valid_chunk_id(id_) - - return id_ in self.__chunks + if id_ == 'FORM': # For backwards compatibility + return True + return super().__contains__(id_) def __getitem__(self, id_): - """Get a chunk from the IFF file""" - - assert_valid_chunk_id(id_) - - try: - return self.__chunks[id_] - except KeyError: - raise KeyError( - "%r has no %r chunk" % (self.__fileobj, id_)) - - def __delitem__(self, id_): - """Remove a chunk from the IFF file""" - - assert_valid_chunk_id(id_) - - self.__chunks.pop(id_).delete() - - def insert_chunk(self, id_): - """Insert a new chunk at the end of the IFF file""" - - assert_valid_chunk_id(id_) - - self.__fileobj.seek(self.__next_offset) - self.__fileobj.write(pack('>4si', id_.ljust(4).encode('ascii'), 0)) - self.__fileobj.seek(self.__next_offset) - chunk = IFFChunk(self.__fileobj, self[u'FORM']) - self[u'FORM']._update_size(self[u'FORM'].data_size + chunk.size) - - self.__chunks[id_] = chunk - self.__next_offset = chunk.offset + chunk.size + if id_ == 'FORM': # For backwards compatibility + return self.root + return super().__getitem__(id_) class AIFFInfo(StreamInfo): @@ -224,7 +130,7 @@ class AIFFInfo(StreamInfo): bitrate (`int`): audio bitrate, in bits per second channels (`int`): The number of audio channels sample_rate (`int`): audio sample rate, in Hz - sample_size (`int`): The audio sample size + bits_per_sample (`int`): The audio sample size """ length = 0 @@ -236,7 +142,7 @@ class AIFFInfo(StreamInfo): def __init__(self, fileobj): """Raises error""" - iff = IFFFile(fileobj) + iff = AIFFFile(fileobj) try: common_chunk = iff[u'COMM'] except KeyError as e: @@ -249,61 +155,30 @@ class AIFFInfo(StreamInfo): info = struct.unpack('>hLh10s', data[:18]) channels, frame_count, sample_size, sample_rate = info - self.sample_rate = int(read_float(sample_rate)) - self.sample_size = sample_size + try: + self.sample_rate = int(read_float(sample_rate)) + except OverflowError: + raise error("Invalid sample rate") + if self.sample_rate < 0: + raise error("Invalid sample rate") + if self.sample_rate != 0: + self.length = frame_count / float(self.sample_rate) + + self.bits_per_sample = sample_size + self.sample_size = sample_size # For backward compatibility self.channels = channels self.bitrate = channels * sample_size * self.sample_rate - self.length = frame_count / float(self.sample_rate) def pprint(self): return u"%d channel AIFF @ %d bps, %s Hz, %.2f seconds" % ( self.channels, self.bitrate, self.sample_rate, self.length) -class _IFFID3(ID3): +class _IFFID3(IffID3): """A AIFF file with ID3v2 tags""" - def _pre_load_header(self, fileobj): - try: - fileobj.seek(IFFFile(fileobj)[u'ID3'].data_offset) - except (InvalidChunk, KeyError): - raise ID3NoHeaderError("No ID3 chunk") - - @convert_error(IOError, error) - @loadfile(writable=True) - def save(self, filething, v2_version=4, v23_sep='/', padding=None): - """Save ID3v2 data to the AIFF file""" - - fileobj = filething.fileobj - - iff_file = IFFFile(fileobj) - - if u'ID3' not in iff_file: - iff_file.insert_chunk(u'ID3') - - chunk = iff_file[u'ID3'] - - try: - data = self._prepare_data( - fileobj, chunk.data_offset, chunk.data_size, v2_version, - v23_sep, padding) - except ID3Error as e: - reraise(error, e, sys.exc_info()[2]) - - new_size = len(data) - new_size += new_size % 2 # pad byte - assert new_size % 2 == 0 - chunk.resize(new_size) - data += (new_size - len(data)) * b'\x00' - assert new_size == len(data) - chunk.write(data) - - @loadfile(writable=True) - def delete(self, filething): - """Completely removes the ID3 chunk from the AIFF file""" - - delete(filething) - self.clear() + def _load_file(self, fileobj): + return AIFFFile(fileobj) @convert_error(IOError, error) @@ -312,7 +187,7 @@ def delete(filething): """Completely removes the ID3 chunk from the AIFF file""" try: - del IFFFile(filething.fileobj)[u'ID3'] + del AIFFFile(filething.fileobj)[u'ID3'] except KeyError: pass diff --git a/libs/common/mutagen/apev2.py b/libs/common/mutagen/apev2.py index 8789d634..a42dc3d1 100644 --- a/libs/common/mutagen/apev2.py +++ b/libs/common/mutagen/apev2.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -32,24 +31,17 @@ __all__ = ["APEv2", "APEv2File", "Open", "delete"] import sys import struct -from collections import MutableSequence +from io import BytesIO +from collections.abc import MutableSequence -from ._compat import (cBytesIO, PY3, text_type, PY2, reraise, swap_to_string, - xrange) from mutagen import Metadata, FileType, StreamInfo from mutagen._util import DictMixin, cdata, delete_bytes, total_ordering, \ - MutagenError, loadfile, convert_error, seek_end, get_size + MutagenError, loadfile, convert_error, seek_end, get_size, reraise def is_valid_apev2_key(key): - if not isinstance(key, text_type): - if PY3: - raise TypeError("APEv2 key must be str") - - try: - key = key.decode('ascii') - except UnicodeDecodeError: - return False + if not isinstance(key, str): + raise TypeError("APEv2 key must be str") # PY26 - Change to set literal syntax (since set is faster than list here) return ((2 <= len(key) <= 255) and (min(key) >= u' ') and @@ -61,7 +53,7 @@ def is_valid_apev2_key(key): # 1: Item contains binary information # 2: Item is a locator of external stored information [e.g. URL] # 3: reserved" -TEXT, BINARY, EXTERNAL = xrange(3) +TEXT, BINARY, EXTERNAL = range(3) HAS_HEADER = 1 << 31 HAS_NO_FOOTER = 1 << 30 @@ -301,9 +293,9 @@ class APEv2(_CIDictProxy, Metadata): def __parse_tag(self, tag, count): """Raises IOError and APEBadItemError""" - fileobj = cBytesIO(tag) + fileobj = BytesIO(tag) - for i in xrange(count): + for i in range(count): tag_data = fileobj.read(8) # someone writes wrong item counts if not tag_data: @@ -330,11 +322,10 @@ class APEv2(_CIDictProxy, Metadata): if key[-1:] == b"\x00": key = key[:-1] - if PY3: - try: - key = key.decode("ascii") - except UnicodeError as err: - reraise(APEBadItemError, err, sys.exc_info()[2]) + try: + key = key.decode("ascii") + except UnicodeError as err: + reraise(APEBadItemError, err, sys.exc_info()[2]) value = fileobj.read(size) if len(value) != size: raise APEBadItemError @@ -346,16 +337,12 @@ class APEv2(_CIDictProxy, Metadata): def __getitem__(self, key): if not is_valid_apev2_key(key): raise KeyError("%r is not a valid APEv2 key" % key) - if PY2: - key = key.encode('ascii') return super(APEv2, self).__getitem__(key) def __delitem__(self, key): if not is_valid_apev2_key(key): raise KeyError("%r is not a valid APEv2 key" % key) - if PY2: - key = key.encode('ascii') super(APEv2, self).__delitem__(key) @@ -383,43 +370,28 @@ class APEv2(_CIDictProxy, Metadata): if not is_valid_apev2_key(key): raise KeyError("%r is not a valid APEv2 key" % key) - if PY2: - key = key.encode('ascii') - if not isinstance(value, _APEValue): # let's guess at the content if we're not already a value... - if isinstance(value, text_type): + if isinstance(value, str): # unicode? we've got to be text. value = APEValue(value, TEXT) elif isinstance(value, list): items = [] for v in value: - if not isinstance(v, text_type): - if PY3: - raise TypeError("item in list not str") - v = v.decode("utf-8") + if not isinstance(v, str): + raise TypeError("item in list not str") items.append(v) # list? text. value = APEValue(u"\0".join(items), TEXT) else: - if PY3: - value = APEValue(value, BINARY) - else: - try: - value.decode("utf-8") - except UnicodeError: - # invalid UTF8 text, probably binary - value = APEValue(value, BINARY) - else: - # valid UTF8, probably text - value = APEValue(value, TEXT) + value = APEValue(value, BINARY) super(APEv2, self).__setitem__(key, value) @convert_error(IOError, error) @loadfile(writable=True, create=True) - def save(self, filething): + def save(self, filething=None): """Save changes to a file. If no filename is given, the one most recently loaded is used. @@ -481,7 +453,7 @@ class APEv2(_CIDictProxy, Metadata): @convert_error(IOError, error) @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """Remove tags from a file.""" fileobj = filething.fileobj @@ -544,7 +516,7 @@ def APEValue(value, kind): class _APEValue(object): - kind = None + kind: int value = None def __init__(self, value, kind=None): @@ -578,7 +550,6 @@ class _APEValue(object): return "%s(%r, %d)" % (type(self).__name__, self.value, self.kind) -@swap_to_string @total_ordering class _APEUtf8Value(_APEValue): @@ -589,11 +560,8 @@ class _APEUtf8Value(_APEValue): reraise(APEBadItemError, e, sys.exc_info()[2]) def _validate(self, value): - if not isinstance(value, text_type): - if PY3: - raise TypeError("value not str") - else: - value = value.decode("utf-8") + if not isinstance(value, str): + raise TypeError("value not str") return value def _write(self): @@ -636,22 +604,16 @@ class APETextValue(_APEUtf8Value, MutableSequence): return self.value.count(u"\0") + 1 def __setitem__(self, index, value): - if not isinstance(value, text_type): - if PY3: - raise TypeError("value not str") - else: - value = value.decode("utf-8") + if not isinstance(value, str): + raise TypeError("value not str") values = list(self) values[index] = value self.value = u"\0".join(values) def insert(self, index, value): - if not isinstance(value, text_type): - if PY3: - raise TypeError("value not str") - else: - value = value.decode("utf-8") + if not isinstance(value, str): + raise TypeError("value not str") values = list(self) values.insert(index, value) @@ -666,7 +628,6 @@ class APETextValue(_APEUtf8Value, MutableSequence): return u" / ".join(self) -@swap_to_string @total_ordering class APEBinaryValue(_APEValue): """An APEv2 binary value.""" diff --git a/libs/common/mutagen/asf/__init__.py b/libs/common/mutagen/asf/__init__.py index 32e1bedb..41e00901 100644 --- a/libs/common/mutagen/asf/__init__.py +++ b/libs/common/mutagen/asf/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005-2006 Joe Wreschnig # Copyright (C) 2006-2007 Lukas Lalinsky # @@ -13,7 +12,6 @@ __all__ = ["ASF", "Open"] from mutagen import FileType, Tags, StreamInfo from mutagen._util import resize_bytes, DictMixin, loadfile, convert_error -from mutagen._compat import string_types, long_, PY3, izip from ._util import error, ASFError, ASFHeaderError from ._objects import HeaderObject, MetadataLibraryObject, MetadataObject, \ @@ -24,7 +22,7 @@ from ._attrs import ASFGUIDAttribute, ASFWordAttribute, ASFQWordAttribute, \ ASFUnicodeAttribute, ASFBaseAttribute, ASFValue -# pyflakes +# flake8 error, ASFError, ASFHeaderError, ASFValue @@ -75,7 +73,7 @@ class ASFInfo(StreamInfo): return s -class ASFTags(list, DictMixin, Tags): +class ASFTags(list, DictMixin, Tags): # type: ignore """ASFTags() Dictionary containing ASF attributes. @@ -89,7 +87,6 @@ class ASFTags(list, DictMixin, Tags): """ - # PY3 only if isinstance(key, slice): return list.__getitem__(self, key) @@ -102,7 +99,6 @@ class ASFTags(list, DictMixin, Tags): def __delitem__(self, key): """Delete all values associated with the key.""" - # PY3 only if isinstance(key, slice): return list.__delitem__(self, key) @@ -129,7 +125,6 @@ class ASFTags(list, DictMixin, Tags): string. """ - # PY3 only if isinstance(key, slice): return list.__setitem__(self, key, values) @@ -139,16 +134,14 @@ class ASFTags(list, DictMixin, Tags): to_append = [] for value in values: if not isinstance(value, ASFBaseAttribute): - if isinstance(value, string_types): + if isinstance(value, str): value = ASFUnicodeAttribute(value) - elif PY3 and isinstance(value, bytes): + elif isinstance(value, bytes): value = ASFByteArrayAttribute(value) elif isinstance(value, bool): value = ASFBoolAttribute(value) elif isinstance(value, int): value = ASFDWordAttribute(value) - elif isinstance(value, long_): - value = ASFQWordAttribute(value) else: raise TypeError("Invalid type %r" % type(value)) to_append.append((key, value)) @@ -163,7 +156,7 @@ class ASFTags(list, DictMixin, Tags): def keys(self): """Return a sequence of all keys in the comment.""" - return self and set(next(izip(*self))) + return self and set(next(zip(*self))) def as_dict(self): """Return a copy of the comment data in a real dict.""" @@ -252,7 +245,7 @@ class ASF(FileType): @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): """save(filething=None, padding=None) Save tag changes back to the loaded file. @@ -319,7 +312,7 @@ class ASF(FileType): raise ASFError @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Args: diff --git a/libs/common/mutagen/asf/_attrs.py b/libs/common/mutagen/asf/_attrs.py index 8111c1c2..0417d9db 100644 --- a/libs/common/mutagen/asf/_attrs.py +++ b/libs/common/mutagen/asf/_attrs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005-2006 Joe Wreschnig # Copyright (C) 2006-2007 Lukas Lalinsky # @@ -9,9 +8,9 @@ import sys import struct +from typing import Dict, Type -from mutagen._compat import swap_to_string, text_type, PY2, reraise -from mutagen._util import total_ordering +from mutagen._util import total_ordering, reraise from ._util import ASFError @@ -19,9 +18,9 @@ from ._util import ASFError class ASFBaseAttribute(object): """Generic attribute.""" - TYPE = None + TYPE: int - _TYPES = {} + _TYPES: "Dict[int, Type[ASFBaseAttribute]]" = {} value = None """The Python value of this attribute (type depends on the class)""" @@ -103,7 +102,6 @@ class ASFBaseAttribute(object): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFUnicodeAttribute(ASFBaseAttribute): """Unicode string attribute. @@ -122,11 +120,8 @@ class ASFUnicodeAttribute(ASFBaseAttribute): reraise(ASFError, e, sys.exc_info()[2]) def _validate(self, value): - if not isinstance(value, text_type): - if PY2: - return value.decode("utf-8") - else: - raise TypeError("%r not str" % value) + if not isinstance(value, str): + raise TypeError("%r not str" % value) return value def _render(self): @@ -142,16 +137,15 @@ class ASFUnicodeAttribute(ASFBaseAttribute): return self.value def __eq__(self, other): - return text_type(self) == other + return str(self) == other def __lt__(self, other): - return text_type(self) < other + return str(self) < other __hash__ = ASFBaseAttribute.__hash__ @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFByteArrayAttribute(ASFBaseAttribute): """Byte array attribute. @@ -194,7 +188,6 @@ class ASFByteArrayAttribute(ASFBaseAttribute): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFBoolAttribute(ASFBaseAttribute): """Bool attribute. @@ -228,10 +221,10 @@ class ASFBoolAttribute(ASFBaseAttribute): return bool(self.value) def __bytes__(self): - return text_type(self.value).encode('utf-8') + return str(self.value).encode('utf-8') def __str__(self): - return text_type(self.value) + return str(self.value) def __eq__(self, other): return bool(self.value) == other @@ -243,7 +236,6 @@ class ASFBoolAttribute(ASFBaseAttribute): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFDWordAttribute(ASFBaseAttribute): """DWORD attribute. @@ -274,10 +266,10 @@ class ASFDWordAttribute(ASFBaseAttribute): return self.value def __bytes__(self): - return text_type(self.value).encode('utf-8') + return str(self.value).encode('utf-8') def __str__(self): - return text_type(self.value) + return str(self.value) def __eq__(self, other): return int(self.value) == other @@ -289,7 +281,6 @@ class ASFDWordAttribute(ASFBaseAttribute): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFQWordAttribute(ASFBaseAttribute): """QWORD attribute. @@ -320,10 +311,10 @@ class ASFQWordAttribute(ASFBaseAttribute): return self.value def __bytes__(self): - return text_type(self.value).encode('utf-8') + return str(self.value).encode('utf-8') def __str__(self): - return text_type(self.value) + return str(self.value) def __eq__(self, other): return int(self.value) == other @@ -335,7 +326,6 @@ class ASFQWordAttribute(ASFBaseAttribute): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFWordAttribute(ASFBaseAttribute): """WORD attribute. @@ -366,10 +356,10 @@ class ASFWordAttribute(ASFBaseAttribute): return self.value def __bytes__(self): - return text_type(self.value).encode('utf-8') + return str(self.value).encode('utf-8') def __str__(self): - return text_type(self.value) + return str(self.value) def __eq__(self, other): return int(self.value) == other @@ -381,7 +371,6 @@ class ASFWordAttribute(ASFBaseAttribute): @ASFBaseAttribute._register -@swap_to_string @total_ordering class ASFGUIDAttribute(ASFBaseAttribute): """GUID attribute.""" diff --git a/libs/common/mutagen/asf/_objects.py b/libs/common/mutagen/asf/_objects.py index 1c15d613..df95bace 100644 --- a/libs/common/mutagen/asf/_objects.py +++ b/libs/common/mutagen/asf/_objects.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005-2006 Joe Wreschnig # Copyright (C) 2006-2007 Lukas Lalinsky # @@ -8,9 +7,9 @@ # (at your option) any later version. import struct +from typing import Dict, Type from mutagen._util import cdata, get_size -from mutagen._compat import text_type, xrange, izip from mutagen._tags import PaddingInfo from ._util import guid2bytes, bytes2guid, CODECS, ASFError, ASFHeaderError @@ -20,8 +19,8 @@ from ._attrs import ASFBaseAttribute, ASFUnicodeAttribute class BaseObject(object): """Base ASF object.""" - GUID = None - _TYPES = {} + GUID: bytes + _TYPES: "Dict[bytes, Type[BaseObject]]" = {} def __init__(self): self.objects = [] @@ -89,7 +88,7 @@ class HeaderObject(BaseObject): remaining_header, num_objects = cls.parse_size(fileobj) remaining_header -= 30 - for i in xrange(num_objects): + for i in range(num_objects): obj_header_size = 24 if remaining_header < obj_header_size: raise ASFHeaderError("invalid header size") @@ -108,13 +107,16 @@ class HeaderObject(BaseObject): try: data = fileobj.read(payload_size) - except OverflowError: + except (OverflowError, MemoryError): # read doesn't take 64bit values raise ASFHeaderError("invalid header size") if len(data) != payload_size: raise ASFHeaderError("truncated") - obj.parse(asf, data) + try: + obj.parse(asf, data) + except struct.error: + raise ASFHeaderError("truncated") header.objects.append(obj) return header @@ -151,7 +153,8 @@ class HeaderObject(BaseObject): # ask the user for padding adjustments file_size = get_size(fileobj) content_size = file_size - available - assert content_size >= 0 + if content_size < 0: + raise ASFHeaderError("truncated content") info = PaddingInfo(available - needed_size, content_size) # add padding @@ -200,7 +203,7 @@ class ContentDescriptionObject(BaseObject): texts.append(None) pos = end - for key, value in izip(self.NAMES, texts): + for key, value in zip(self.NAMES, texts): if value is not None: value = ASFUnicodeAttribute(value=value) asf._tags.setdefault(self.GUID, []).append((key, value)) @@ -209,7 +212,7 @@ class ContentDescriptionObject(BaseObject): def render_text(name): value = asf.to_content_description.get(name) if value is not None: - return text_type(value).encode("utf-16-le") + b"\x00\x00" + return str(value).encode("utf-16-le") + b"\x00\x00" else: return b"" @@ -228,7 +231,7 @@ class ExtendedContentDescriptionObject(BaseObject): super(ExtendedContentDescriptionObject, self).parse(asf, data) num_attributes, = struct.unpack("= 0 asf.info.length = max((length / 10000000.0) - (preroll / 1000.0), 0.0) @@ -319,7 +324,7 @@ class CodecListObject(BaseObject): offset = 16 count, offset = cdata.uint32_le_from(data, offset) - for i in xrange(count): + for i in range(count): try: offset, type_, name, desc, codec = \ self._parse_entry(data, offset) @@ -377,6 +382,8 @@ class HeaderExtensionObject(BaseObject): while datapos < datasize: guid, size = struct.unpack( "<16sQ", data[22 + datapos:22 + datapos + 24]) + if size < 1: + raise ASFHeaderError("invalid size in header extension") obj = BaseObject._get_object(guid) obj.parse(asf, data[22 + datapos + 24:22 + datapos + size]) self.objects.append(obj) @@ -407,7 +414,7 @@ class MetadataObject(BaseObject): super(MetadataObject, self).parse(asf, data) num_attributes, = struct.unpack("4sQ', header) + + @classmethod + def get_class(cls, id): + if id in DSDIFFListChunk.LIST_CHUNK_IDS: + return DSDIFFListChunk + elif id == 'DST': + return DSTChunk + else: + return cls + + def write_new_header(self, id_, size): + self._fileobj.write(struct.pack('>4sQ', id_, size)) + + def write_size(self): + self._fileobj.write(struct.pack('>Q', self.data_size)) + + +class DSDIFFListChunk(DSDIFFChunk, IffContainerChunkMixin): + """A DSDIFF chunk containing other chunks. + """ + + LIST_CHUNK_IDS = ['FRM8', 'PROP'] + + def parse_next_subchunk(self): + return DSDIFFChunk.parse(self._fileobj, self) + + def __init__(self, fileobj, id, data_size, parent_chunk): + if id not in self.LIST_CHUNK_IDS: + raise InvalidChunk('Not a list chunk: %s' % id) + + DSDIFFChunk.__init__(self, fileobj, id, data_size, parent_chunk) + self.init_container() + + +class DSTChunk(DSDIFFChunk, IffContainerChunkMixin): + """A DSDIFF chunk containing other chunks. + """ + + def parse_next_subchunk(self): + return DSDIFFChunk.parse(self._fileobj, self) + + def __init__(self, fileobj, id, data_size, parent_chunk): + if id != 'DST': + raise InvalidChunk('Not a DST chunk: %s' % id) + + DSDIFFChunk.__init__(self, fileobj, id, data_size, parent_chunk) + self.init_container(name_size=0) + + +class DSDIFFFile(IffFile): + """Representation of a DSDIFF file""" + + def __init__(self, fileobj): + super().__init__(DSDIFFChunk, fileobj) + + if self.root.id != u'FRM8': + raise InvalidChunk("Root chunk must be a FRM8 chunk, got %r" + % self.root) + + +class DSDIFFInfo(StreamInfo): + + """DSDIFF stream information. + + Attributes: + channels (`int`): number of audio channels + length (`float`): file length in seconds, as a float + sample_rate (`int`): audio sampling rate in Hz + bits_per_sample (`int`): audio sample size (for DSD this is always 1) + bitrate (`int`): audio bitrate, in bits per second + compression (`str`): DSD (uncompressed) or DST + """ + + channels = 0 + length = 0 + sample_rate = 0 + bits_per_sample = 1 + bitrate = 0 + compression = None + + @convert_error(IOError, error) + def __init__(self, fileobj): + """Raises error""" + + iff = DSDIFFFile(fileobj) + try: + prop_chunk = iff['PROP'] + except KeyError as e: + raise error(str(e)) + + if prop_chunk.name == 'SND ': + for chunk in prop_chunk.subchunks(): + if chunk.id == 'FS' and chunk.data_size == 4: + data = chunk.read() + if len(data) < 4: + raise InvalidChunk("Not enough data in FS chunk") + self.sample_rate, = struct.unpack('>L', data[:4]) + elif chunk.id == 'CHNL' and chunk.data_size >= 2: + data = chunk.read() + if len(data) < 2: + raise InvalidChunk("Not enough data in CHNL chunk") + self.channels, = struct.unpack('>H', data[:2]) + elif chunk.id == 'CMPR' and chunk.data_size >= 4: + data = chunk.read() + if len(data) < 4: + raise InvalidChunk("Not enough data in CMPR chunk") + compression_id, = struct.unpack('>4s', data[:4]) + self.compression = compression_id.decode('ascii').rstrip() + + if self.sample_rate < 0: + raise error("Invalid sample rate") + + if self.compression == 'DSD': # not compressed + try: + dsd_chunk = iff['DSD'] + except KeyError as e: + raise error(str(e)) + + # DSD data has one bit per sample. Eight samples of a channel + # are clustered together for a channel byte. For multiple channels + # the channel bytes are interleaved (in the order specified in the + # CHNL chunk). See DSDIFF spec chapter 3.3. + sample_count = dsd_chunk.data_size * 8 / (self.channels or 1) + + if self.sample_rate != 0: + self.length = sample_count / float(self.sample_rate) + + self.bitrate = (self.channels * self.bits_per_sample + * self.sample_rate) + elif self.compression == 'DST': + try: + dst_frame = iff['DST'] + dst_frame_info = dst_frame['FRTE'] + except KeyError as e: + raise error(str(e)) + + if dst_frame_info.data_size >= 6: + data = dst_frame_info.read() + if len(data) < 6: + raise InvalidChunk("Not enough data in FRTE chunk") + frame_count, frame_rate = struct.unpack('>LH', data[:6]) + if frame_rate: + self.length = frame_count / frame_rate + + if frame_count: + dst_data_size = dst_frame.data_size - dst_frame_info.size + avg_frame_size = dst_data_size / frame_count + self.bitrate = avg_frame_size * 8 * frame_rate + + def pprint(self): + return u"%d channel DSDIFF (%s) @ %d bps, %s Hz, %.2f seconds" % ( + self.channels, self.compression, self.bitrate, self.sample_rate, + self.length) + + +class _DSDIFFID3(IffID3): + """A DSDIFF file with ID3v2 tags""" + + def _load_file(self, fileobj): + return DSDIFFFile(fileobj) + + +@convert_error(IOError, error) +@loadfile(method=False, writable=True) +def delete(filething): + """Completely removes the ID3 chunk from the DSDIFF file""" + + try: + del DSDIFFFile(filething.fileobj)[u'ID3'] + except KeyError: + pass + + +class DSDIFF(FileType): + """DSDIFF(filething) + + An DSDIFF audio file. + + For tagging ID3v2 data is added to a chunk with the ID "ID3 ". + + Arguments: + filething (filething) + + Attributes: + tags (`mutagen.id3.ID3`) + info (`DSDIFFInfo`) + """ + + _mimes = ["audio/x-dff"] + + @convert_error(IOError, error) + @loadfile() + def load(self, filething, **kwargs): + fileobj = filething.fileobj + + try: + self.tags = _DSDIFFID3(fileobj, **kwargs) + except ID3NoHeaderError: + self.tags = None + except ID3Error as e: + raise error(e) + else: + self.tags.filename = self.filename + + fileobj.seek(0, 0) + self.info = DSDIFFInfo(fileobj) + + def add_tags(self): + """Add empty ID3 tags to the file.""" + if self.tags is None: + self.tags = _DSDIFFID3() + else: + raise error("an ID3 tag already exists") + + @staticmethod + def score(filename, fileobj, header): + return header.startswith(b"FRM8") * 2 + endswith(filename, ".dff") + + +Open = DSDIFF diff --git a/libs/common/mutagen/dsf.py b/libs/common/mutagen/dsf.py index ed5faae2..121a01ba 100644 --- a/libs/common/mutagen/dsf.py +++ b/libs/common/mutagen/dsf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2017 Boris Pruessmann # # This program is free software; you can redistribute it and/or modify @@ -11,11 +10,11 @@ import sys import struct - -from ._compat import cBytesIO, reraise, endswith +from io import BytesIO from mutagen import FileType, StreamInfo -from mutagen._util import cdata, MutagenError, loadfile, convert_error +from mutagen._util import cdata, MutagenError, loadfile, \ + convert_error, reraise, endswith from mutagen.id3 import ID3 from mutagen.id3._util import ID3NoHeaderError, error as ID3Error @@ -80,7 +79,7 @@ class DSDChunk(DSFChunk): self.offset_metdata_chunk = cdata.ulonglong_le(data[20:28]) def write(self): - f = cBytesIO() + f = BytesIO() f.write(self.chunk_header) f.write(struct.pack("I", self.min_blocksize)[-2:]) f.write(struct.pack(">I", self.max_blocksize)[-2:]) f.write(struct.pack(">I", self.min_framesize)[-3:]) @@ -244,11 +243,11 @@ class StreamInfo(MetadataBlock, mutagen.StreamInfo): byte = (self.sample_rate & 0xF) << 4 byte += ((self.channels - 1) & 7) << 1 byte += ((self.bits_per_sample - 1) >> 4) & 1 - f.write(chr_(byte)) + f.write(bchr(byte)) # 4 bits of bps, 4 of sample count byte = ((self.bits_per_sample - 1) & 0xF) << 4 byte += (self.total_samples >> 32) & 0xF - f.write(chr_(byte)) + f.write(bchr(byte)) # last 32 of sample count f.write(struct.pack(">I", self.total_samples & 0xFFFFFFFF)) # MD5 signature @@ -284,6 +283,9 @@ class SeekPoint(tuple): return super(SeekPoint, cls).__new__( cls, (first_sample, byte_offset, num_samples)) + def __getnewargs__(self): + return self.first_sample, self.byte_offset, self.num_samples + first_sample = property(lambda self: self[0]) byte_offset = property(lambda self: self[1]) num_samples = property(lambda self: self[2]) @@ -322,7 +324,7 @@ class SeekTable(MetadataBlock): sp = data.tryread(self.__SEEKPOINT_SIZE) def write(self): - f = cBytesIO() + f = BytesIO() for seekpoint in self.seekpoints: packed = struct.pack( self.__SEEKPOINT_FORMAT, @@ -371,7 +373,7 @@ class CueSheetTrackIndex(tuple): """ def __new__(cls, index_number, index_offset): - return super(cls, CueSheetTrackIndex).__new__( + return super(CueSheetTrackIndex, cls).__new__( cls, (index_number, index_offset)) index_number = property(lambda self: self[0]) @@ -394,7 +396,7 @@ class CueSheetTrack(object): isrc (`mutagen.text`): ISRC code, exactly 12 characters type (`int`): 0 for audio, 1 for digital data pre_emphasis (`bool`): true if the track is recorded with pre-emphasis - indexes (List[`mutagen.flac.CueSheetTrackIndex`]): + indexes (list[CueSheetTrackIndex]): list of CueSheetTrackIndex objects """ @@ -442,9 +444,9 @@ class CueSheet(MetadataBlock): lead_in_samples (`int`): number of lead-in samples compact_disc (`bool`): true if the cuesheet corresponds to a compact disc - tracks (List[`mutagen.flac.CueSheetTrack`]): + tracks (list[CueSheetTrack]): list of CueSheetTrack objects - lead_out (`mutagen.flac.CueSheetTrack` or `None`): + lead_out (`CueSheetTrack` or `None`): lead-out as CueSheetTrack or None if lead-out was not found """ @@ -484,7 +486,7 @@ class CueSheet(MetadataBlock): self.lead_in_samples = lead_in_samples self.compact_disc = bool(flags & 0x80) self.tracks = [] - for i in xrange(num_tracks): + for i in range(num_tracks): track = data.read(self.__CUESHEET_TRACK_SIZE) start_offset, track_number, isrc_padded, flags, num_indexes = \ struct.unpack(self.__CUESHEET_TRACK_FORMAT, track) @@ -493,7 +495,7 @@ class CueSheet(MetadataBlock): pre_emphasis = bool(flags & 0x40) val = CueSheetTrack( track_number, start_offset, isrc, type_, pre_emphasis) - for j in xrange(num_indexes): + for j in range(num_indexes): index = data.read(self.__CUESHEET_TRACKINDEX_SIZE) index_offset, index_number = struct.unpack( self.__CUESHEET_TRACKINDEX_FORMAT, index) @@ -502,7 +504,7 @@ class CueSheet(MetadataBlock): self.tracks.append(val) def write(self): - f = cBytesIO() + f = BytesIO() flags = 0 if self.compact_disc: flags |= 0x80 @@ -608,7 +610,7 @@ class Picture(MetadataBlock): self.data = data.read(length) def write(self): - f = cBytesIO() + f = BytesIO() mime = self.mime.encode('UTF-8') f.write(struct.pack('>2I', self.type, len(mime))) f.write(mime) @@ -678,14 +680,13 @@ class FLAC(mutagen.FileType): Attributes: cuesheet (`CueSheet`): if any or `None` seektable (`SeekTable`): if any or `None` - pictures (List[`Picture`]): list of embedded pictures + pictures (list[Picture]): list of embedded pictures info (`StreamInfo`) tags (`mutagen._vorbis.VCommentDict`) """ _mimes = ["audio/flac", "audio/x-flac", "application/x-flac"] - info = None tags = None METADATA_BLOCKS = [StreamInfo, Padding, None, SeekTable, VCFLACDict, @@ -711,7 +712,7 @@ class FLAC(mutagen.FileType): if block_type._distrust_size: # Some jackass is writing broken Metadata block length # for Vorbis comment blocks, and the FLAC reference - # implementaton can parse them (mostly by accident), + # implementation can parse them (mostly by accident), # so we have to too. Instead of parsing the size # given, parse an actual Vorbis comment, leaving # fileobj in the right position. @@ -732,7 +733,9 @@ class FLAC(mutagen.FileType): if self.tags is None: self.tags = block else: - raise FLACVorbisError("> 1 Vorbis comment block found") + # https://github.com/quodlibet/mutagen/issues/377 + # Something writes multiple and metaflac doesn't care + pass elif block.code == CueSheet.code: if self.cuesheet is None: self.cuesheet = block @@ -756,19 +759,21 @@ class FLAC(mutagen.FileType): add_vorbiscomment = add_tags + @convert_error(IOError, error) @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """Remove Vorbis comments from a file. If no filename is given, the one most recently loaded is used. """ if self.tags is not None: - self.metadata_blocks.remove(self.tags) - try: - self.save(filething, padding=lambda x: 0) - finally: - self.metadata_blocks.append(self.tags) + temp_blocks = [ + b for b in self.metadata_blocks if b.code != VCFLACDict.code] + self._save(filething, temp_blocks, False, padding=lambda x: 0) + self.metadata_blocks[:] = [ + b for b in self.metadata_blocks + if b.code != VCFLACDict.code or b is self.tags] self.tags.clear() vc = property(lambda s: s.tags, doc="Alias for tags; don't use this.") @@ -791,7 +796,7 @@ class FLAC(mutagen.FileType): pass try: - self.metadata_blocks[0].length + self.info.length except (AttributeError, IndexError): raise FLACNoHeaderError("Stream info block not found") @@ -805,7 +810,11 @@ class FLAC(mutagen.FileType): @property def info(self): - return self.metadata_blocks[0] + streaminfo_blocks = [ + block for block in self.metadata_blocks + if block.code == StreamInfo.code + ] + return streaminfo_blocks[0] def add_picture(self, picture): """Add a new picture to the file. @@ -823,16 +832,11 @@ class FLAC(mutagen.FileType): @property def pictures(self): - """ - Returns: - List[`Picture`]: List of embedded pictures - """ - return [b for b in self.metadata_blocks if b.code == Picture.code] @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, deleteid3=False, padding=None): + def save(self, filething=None, deleteid3=False, padding=None): """Save metadata blocks to a file. Args: @@ -843,6 +847,9 @@ class FLAC(mutagen.FileType): If no filename is given, the one most recently loaded is used. """ + self._save(filething, self.metadata_blocks, deleteid3, padding) + + def _save(self, filething, metadata_blocks, deleteid3, padding): f = StrictFileObject(filething.fileobj) header = self.__check_header(f, filething.name) audio_offset = self.__find_audio_offset(f) @@ -857,7 +864,7 @@ class FLAC(mutagen.FileType): content_size = get_size(f) - audio_offset assert content_size >= 0 data = MetadataBlock._writeblocks( - self.metadata_blocks, available, content_size, padding) + metadata_blocks, available, content_size, padding) data_size = len(data) resize_bytes(filething.fileobj, available, data_size, header) diff --git a/libs/common/mutagen/id3/__init__.py b/libs/common/mutagen/id3/__init__.py index 9033c76c..b70638bd 100644 --- a/libs/common/mutagen/id3/__init__.py +++ b/libs/common/mutagen/id3/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # 2006 Lukas Lalinsky # 2013 Christoph Reiter @@ -61,7 +60,7 @@ from ._util import ID3EncryptionUnsupportedError, ID3JunkFrameError, \ # support open(filename) as interface Open = ID3 -# pyflakes +# flake8 ID3, ID3FileType, delete, ID3v1SaveOptions, Encoding, PictureType, CTOCFlags, ID3TimeStamp, Frames, Frames_2_2, Frame, TextFrame, UrlFrame, UrlFrameU, TimeStampTextFrame, BinaryFrame, NumericPartTextFrame, NumericTextFrame, diff --git a/libs/common/mutagen/id3/_file.py b/libs/common/mutagen/id3/_file.py index cb8794fc..647db3f3 100644 --- a/libs/common/mutagen/id3/_file.py +++ b/libs/common/mutagen/id3/_file.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # 2006 Lukas Lalinsky # 2013 Christoph Reiter @@ -53,8 +52,8 @@ class ID3(ID3Tags, mutagen.Metadata): filething (filething): or `None` Attributes: - version (Tuple[int]): ID3 tag version as a tuple - unknown_frames (List[bytes]): raw frame data of any unknown frames + version (tuple[int]): ID3 tag version as a tuple + unknown_frames (list[bytes]): raw frame data of any unknown frames found size (int): the total size of the ID3 tag, including the header """ @@ -78,8 +77,6 @@ class ID3(ID3Tags, mutagen.Metadata): @property def version(self): - """`tuple`: ID3 tag version as a tuple (of the loaded file)""" - if self._header is not None: return self._header.version return self._version @@ -112,10 +109,9 @@ class ID3(ID3Tags, mutagen.Metadata): @convert_error(IOError, error) @loadfile() - def load(self, filething, known_frames=None, translate=True, v2_version=4): - """load(filething, known_frames=None, translate=True, v2_version=4) - - Load tags from a filename. + def load(self, filething, known_frames=None, translate=True, v2_version=4, + load_v1=True): + """Load tags from a filename. Args: filename (filething): filename or file object to load tag data from @@ -126,6 +122,11 @@ class ID3(ID3Tags, mutagen.Metadata): call update_to_v23() / update_to_v24() manually. v2_version (int): if update_to_v23 or update_to_v24 get called (3 or 4) + load_v1 (bool): Load tags from ID3v1 header if present. If both + ID3v1 and ID3v2 headers are present, combine the tags from + the two, with ID3v2 having precedence. + + .. versionadded:: 1.42 Example of loading a custom frame:: @@ -149,13 +150,17 @@ class ID3(ID3Tags, mutagen.Metadata): try: self._header = ID3Header(fileobj) except (ID3NoHeaderError, ID3UnsupportedVersionError): - frames, offset = find_id3v1(fileobj) + if not load_v1: + raise + + frames, offset = find_id3v1(fileobj, v2_version, known_frames) if frames is None: raise self.version = ID3Header._V11 for v in frames.values(): - self.add(v) + if len(self.getall(v.HashKey)) == 0: + self.add(v) else: # XXX: attach to the header object so we have it in spec parsing.. if known_frames is not None: @@ -165,6 +170,14 @@ class ID3(ID3Tags, mutagen.Metadata): remaining_data = self._read(self._header, data) self._padding = len(remaining_data) + if load_v1: + v1v2_ver = 4 if self.version[1] == 4 else 3 + frames, offset = find_id3v1(fileobj, v1v2_ver, known_frames) + if frames: + for v in frames.values(): + if len(self.getall(v.HashKey)) == 0: + self.add(v) + if translate: if v2_version == 3: self.update_to_v23() @@ -204,13 +217,14 @@ class ID3(ID3Tags, mutagen.Metadata): @convert_error(IOError, error) @loadfile(writable=True, create=True) - def save(self, filething, v1=1, v2_version=4, v23_sep='/', padding=None): + def save(self, filething=None, v1=1, v2_version=4, v23_sep='/', + padding=None): """save(filething=None, v1=1, v2_version=4, v23_sep='/', padding=None) Save changes to a file. Args: - filename (fspath): + filething (filething): Filename to save the tag to. If no filename is given, the one most recently loaded is used. v1 (ID3v1SaveOptions): @@ -268,7 +282,7 @@ class ID3(ID3Tags, mutagen.Metadata): f.truncate() @loadfile(writable=True) - def delete(self, filething, delete_v1=True, delete_v2=True): + def delete(self, filething=None, delete_v1=True, delete_v2=True): """delete(filething=None, delete_v1=True, delete_v2=True) Remove tags from a file. diff --git a/libs/common/mutagen/id3/_frames.py b/libs/common/mutagen/id3/_frames.py index f50752aa..e30ba20b 100644 --- a/libs/common/mutagen/id3/_frames.py +++ b/libs/common/mutagen/id3/_frames.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -8,6 +7,7 @@ import zlib from struct import unpack +from typing import Sequence from ._util import ID3JunkFrameError, ID3EncryptionUnsupportedError, unsynch, \ ID3SaveConfig, error @@ -17,9 +17,7 @@ from ._specs import BinaryDataSpec, StringSpec, Latin1TextSpec, \ VolumeAdjustmentSpec, ChannelSpec, MultiSpec, SynchronizedTextSpec, \ KeyEventSpec, TimeStampSpec, EncodedNumericPartTextSpec, \ EncodedNumericTextSpec, SpecError, PictureTypeSpec, ID3FramesSpec, \ - Latin1TextListSpec, CTOCFlagsSpec, FrameIDSpec, RVASpec -from .._compat import text_type, string_types, swap_to_string, iteritems, \ - izip, itervalues + Latin1TextListSpec, CTOCFlagsSpec, FrameIDSpec, RVASpec, Spec def _bytes2key(b): @@ -51,8 +49,8 @@ class Frame(object): FLAG24_UNSYNCH = 0x0002 FLAG24_DATALEN = 0x0001 - _framespec = [] - _optionalspec = [] + _framespec: Sequence[Spec] = [] + _optionalspec: Sequence[Spec] = [] def __init__(self, *args, **kwargs): if len(args) == 1 and len(kwargs) == 0 and \ @@ -61,7 +59,7 @@ class Frame(object): # ask the sub class to fill in our data other._to_other(self) else: - for checker, val in izip(self._framespec, args): + for checker, val in zip(self._framespec, args): setattr(self, checker.name, val) for checker in self._framespec[len(args):]: setattr(self, checker.name, @@ -277,6 +275,8 @@ class Frame(object): elif header.version >= header._V23: if tflags & Frame.FLAG23_COMPRESS: + if len(data) < 4: + raise ID3JunkFrameError('frame too small: %r' % data) usize, = unpack('>L', data[:4]) data = data[4:] if tflags & Frame.FLAG23_ENCRYPT: @@ -291,7 +291,7 @@ class Frame(object): frame._readData(header, data) return frame - def __hash__(self): + def __hash__(self: object): raise TypeError("Frame objects are unhashable") @@ -330,7 +330,7 @@ class CHAP(Frame): def _pprint(self): frame_pprint = u"" - for frame in itervalues(self.sub_frames): + for frame in self.sub_frames.values(): for line in frame.pprint().splitlines(): frame_pprint += "\n" + " " * 4 + line return u"%s time=%d..%d offset=%d..%d%s" % ( @@ -377,7 +377,6 @@ class CTOC(Frame): u",".join(self.child_element_ids), frame_pprint) -@swap_to_string class TextFrame(Frame): """Text strings. @@ -399,7 +398,7 @@ class TextFrame(Frame): ] def __bytes__(self): - return text_type(self).encode('utf-8') + return str(self).encode('utf-8') def __str__(self): return u'\u0000'.join(self.text) @@ -407,8 +406,8 @@ class TextFrame(Frame): def __eq__(self, other): if isinstance(other, bytes): return bytes(self) == other - elif isinstance(other, text_type): - return text_type(self) == other + elif isinstance(other, str): + return str(self) == other return self.text == other __hash__ = Frame.__hash__ @@ -481,7 +480,6 @@ class NumericPartTextFrame(TextFrame): return int(self.text[0].split("/")[0]) -@swap_to_string class TimeStampTextFrame(TextFrame): """A list of time stamps. @@ -495,7 +493,7 @@ class TimeStampTextFrame(TextFrame): ] def __bytes__(self): - return text_type(self).encode('utf-8') + return str(self).encode('utf-8') def __str__(self): return u','.join([stamp.text for stamp in self.text]) @@ -504,7 +502,6 @@ class TimeStampTextFrame(TextFrame): return u" / ".join([stamp.text for stamp in self.text]) -@swap_to_string class UrlFrame(Frame): """A frame containing a URL string. @@ -517,7 +514,7 @@ class UrlFrame(Frame): ASCII. """ - _framespec = [ + _framespec: Sequence[Spec] = [ Latin1TextSpec('url'), ] @@ -587,7 +584,7 @@ class TCON(TextFrame): if genreid: for gid in genreid[1:-1].split(")("): if gid.isdigit() and int(gid) < len(self.GENRES): - gid = text_type(self.GENRES[int(gid)]) + gid = str(self.GENRES[int(gid)]) newgenres.append(gid) elif gid == "CR": newgenres.append(u"Cover") @@ -608,7 +605,7 @@ class TCON(TextFrame): return genres def __set_genres(self, genres): - if isinstance(genres, string_types): + if isinstance(genres, str): genres = [genres] self.text = [self.__decode(g) for g in genres] @@ -1044,7 +1041,6 @@ class SYTC(Frame): __hash__ = Frame.__hash__ -@swap_to_string class USLT(Frame): """Unsynchronised lyrics/text transcription. @@ -1078,7 +1074,6 @@ class USLT(Frame): return "%s=%s=%s" % (self.desc, self.lang, self.text) -@swap_to_string class SYLT(Frame): """Synchronised lyrics/text.""" @@ -1095,16 +1090,21 @@ class SYLT(Frame): def HashKey(self): return '%s:%s:%s' % (self.FrameID, self.desc, self.lang) + def _pprint(self): + return str(self) + def __eq__(self, other): return str(self) == other __hash__ = Frame.__hash__ def __str__(self): - return u"".join(text for (text, time) in self.text) + unit = 'fr' if self.format == 1 else 'ms' + return u"\n".join("[{0}{1}]: {2}".format(time, unit, text) + for (text, time) in self.text) def __bytes__(self): - return text_type(self).encode("utf-8") + return str(self).encode("utf-8") class COMM(TextFrame): @@ -1279,7 +1279,7 @@ class APIC(Frame): return other def _pprint(self): - type_desc = text_type(self.type) + type_desc = str(self.type) if hasattr(self.type, "_pprint"): type_desc = self.type._pprint() @@ -1309,7 +1309,7 @@ class PCNT(Frame): return self.count def _pprint(self): - return text_type(self.count) + return str(self.count) class PCST(Frame): @@ -1328,7 +1328,7 @@ class PCST(Frame): return self.value def _pprint(self): - return text_type(self.value) + return str(self.value) class POPM(Frame): @@ -1432,7 +1432,6 @@ class RBUF(Frame): return self.size -@swap_to_string class AENC(Frame): """Audio encryption. @@ -1549,7 +1548,6 @@ class UFID(Frame): return "%s=%r" % (self.owner, self.data) -@swap_to_string class USER(Frame): """Terms of use. @@ -1585,7 +1583,6 @@ class USER(Frame): return "%r=%s" % (self.lang, self.text) -@swap_to_string class OWNE(Frame): """Ownership frame.""" @@ -1636,7 +1633,6 @@ class COMR(Frame): __hash__ = Frame.__hash__ -@swap_to_string class ENCR(Frame): """Encryption method registration. @@ -1663,7 +1659,6 @@ class ENCR(Frame): __hash__ = Frame.__hash__ -@swap_to_string class GRID(Frame): """Group identification registration.""" @@ -1692,7 +1687,6 @@ class GRID(Frame): __hash__ = Frame.__hash__ -@swap_to_string class PRIV(Frame): """Private frame.""" @@ -1718,7 +1712,6 @@ class PRIV(Frame): __hash__ = Frame.__hash__ -@swap_to_string class SIGN(Frame): """Signature frame.""" @@ -2130,7 +2123,7 @@ Frames_2_2 = {} k, v = None, None -for k, v in iteritems(globals()): +for k, v in globals().items(): if isinstance(v, type) and issubclass(v, Frame): v.__module__ = "mutagen.id3" diff --git a/libs/common/mutagen/id3/_id3v1.py b/libs/common/mutagen/id3/_id3v1.py index d41d00d0..4e1ca05f 100644 --- a/libs/common/mutagen/id3/_id3v1.py +++ b/libs/common/mutagen/id3/_id3v1.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # 2006 Lukas Lalinsky # 2013 Christoph Reiter @@ -11,22 +10,32 @@ import errno from struct import error as StructError, unpack -from mutagen._util import chr_, text_type +from mutagen._util import bchr -from ._frames import TCON, TRCK, COMM, TDRC, TALB, TPE1, TIT2 +from ._frames import TCON, TRCK, COMM, TDRC, TYER, TALB, TPE1, TIT2 -def find_id3v1(fileobj): +def find_id3v1(fileobj, v2_version=4, known_frames=None): """Returns a tuple of (id3tag, offset_to_end) or (None, 0) offset mainly because we used to write too short tags in some cases and we need the offset to delete them. + + v2_version: Decides whether ID3v2.3 or ID3v2.4 tags + should be returned. Must be 3 or 4. + + known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame + IDs to Frame objects """ + if v2_version not in (3, 4): + raise ValueError("Only 3 and 4 possible for v2_version") + # id3v1 is always at the end (after apev2) extra_read = b"APETAGEX".index(b"TAG") + old_pos = fileobj.tell() try: fileobj.seek(-128 - extra_read, 2) except IOError as e: @@ -38,6 +47,7 @@ def find_id3v1(fileobj): raise data = fileobj.read(128 + extra_read) + fileobj.seek(old_pos, 0) try: idx = data.index(b"TAG") except ValueError: @@ -53,7 +63,7 @@ def find_id3v1(fileobj): if idx == ape_idx + extra_read: return (None, 0) - tag = ParseID3v1(data[idx:]) + tag = ParseID3v1(data[idx:], v2_version, known_frames) if tag is None: return (None, 0) @@ -62,12 +72,21 @@ def find_id3v1(fileobj): # ID3v1.1 support. -def ParseID3v1(data): - """Parse an ID3v1 tag, returning a list of ID3v2.4 frames. +def ParseID3v1(data, v2_version=4, known_frames=None): + """Parse an ID3v1 tag, returning a list of ID3v2 frames Returns a {frame_name: frame} dict or None. + + v2_version: Decides whether ID3v2.3 or ID3v2.4 tags + should be returned. Must be 3 or 4. + + known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame + IDs to Frame objects """ + if v2_version not in (3, 4): + raise ValueError("Only 3 and 4 possible for v2_version") + try: data = data[data.index(b"TAG"):] except ValueError: @@ -97,23 +116,45 @@ def ParseID3v1(data): title, artist, album, year, comment = map( fix, [title, artist, album, year, comment]) + frame_class = { + "TIT2": TIT2, + "TPE1": TPE1, + "TALB": TALB, + "TYER": TYER, + "TDRC": TDRC, + "COMM": COMM, + "TRCK": TRCK, + "TCON": TCON, + } + for key in frame_class: + if known_frames is not None: + if key in known_frames: + frame_class[key] = known_frames[key] + else: + frame_class[key] = None + frames = {} - if title: - frames["TIT2"] = TIT2(encoding=0, text=title) - if artist: - frames["TPE1"] = TPE1(encoding=0, text=[artist]) - if album: - frames["TALB"] = TALB(encoding=0, text=album) + if title and frame_class["TIT2"]: + frames["TIT2"] = frame_class["TIT2"](encoding=0, text=title) + if artist and frame_class["TPE1"]: + frames["TPE1"] = frame_class["TPE1"](encoding=0, text=[artist]) + if album and frame_class["TALB"]: + frames["TALB"] = frame_class["TALB"](encoding=0, text=album) if year: - frames["TDRC"] = TDRC(encoding=0, text=year) - if comment: - frames["COMM"] = COMM( + if v2_version == 3 and frame_class["TYER"]: + frames["TYER"] = frame_class["TYER"](encoding=0, text=year) + elif frame_class["TDRC"]: + frames["TDRC"] = frame_class["TDRC"](encoding=0, text=year) + if comment and frame_class["COMM"]: + frames["COMM"] = frame_class["COMM"]( encoding=0, lang="eng", desc="ID3v1 Comment", text=comment) + # Don't read a track number if it looks like the comment was # padded with spaces instead of nulls (thanks, WinAmp). - if track and ((track != 32) or (data[-3] == b'\x00'[0])): + if (track and frame_class["TRCK"] and + ((track != 32) or (data[-3] == b'\x00'[0]))): frames["TRCK"] = TRCK(encoding=0, text=str(track)) - if genre != 255: + if genre != 255 and frame_class["TCON"]: frames["TCON"] = TCON(encoding=0, text=str(genre)) return frames @@ -139,7 +180,7 @@ def MakeID3v1(id3): if "TRCK" in id3: try: - v1["track"] = chr_(+id3["TRCK"]) + v1["track"] = bchr(+id3["TRCK"]) except ValueError: v1["track"] = b"\x00" else: @@ -152,14 +193,14 @@ def MakeID3v1(id3): pass else: if genre in TCON.GENRES: - v1["genre"] = chr_(TCON.GENRES.index(genre)) + v1["genre"] = bchr(TCON.GENRES.index(genre)) if "genre" not in v1: v1["genre"] = b"\xff" if "TDRC" in id3: - year = text_type(id3["TDRC"]).encode('ascii') + year = str(id3["TDRC"]).encode('ascii') elif "TYER" in id3: - year = text_type(id3["TYER"]).encode('ascii') + year = str(id3["TYER"]).encode('ascii') else: year = b"" v1["year"] = (year + b"\x00\x00\x00\x00")[:4] diff --git a/libs/common/mutagen/id3/_specs.py b/libs/common/mutagen/id3/_specs.py index 63784333..e8f968b6 100644 --- a/libs/common/mutagen/id3/_specs.py +++ b/libs/common/mutagen/id3/_specs.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # # This program is free software; you can redistribute it and/or modify @@ -10,10 +9,8 @@ import struct import codecs from struct import unpack, pack -from .._compat import text_type, chr_, PY3, swap_to_string, string_types, \ - xrange -from .._util import total_ordering, decode_terminated, enum, izip, flags, \ - cdata, encode_endian, intround +from .._util import total_ordering, decode_terminated, enum, flags, \ + cdata, encode_endian, intround, bchr from ._util import BitPaddedInt, is_valid_frame_id @@ -87,7 +84,7 @@ class PictureType(object): """Publisher/Studio logotype""" def _pprint(self): - return text_type(self).split(".", 1)[-1].lower().replace("_", " ") + return str(self).split(".", 1)[-1].lower().replace("_", " ") @flags @@ -165,11 +162,11 @@ class ByteSpec(Spec): return bytearray(data)[0], data[1:] def write(self, config, frame, value): - return chr_(value) + return bchr(value) def validate(self, frame, value): if value is not None: - chr_(value) + bchr(value) return value @@ -289,26 +286,22 @@ class StringSpec(Spec): except UnicodeDecodeError: raise SpecError("not ascii") else: - if PY3: - chunk = ascii + chunk = ascii return chunk, data[s.len:] def write(self, config, frame, value): - if PY3: - value = value.encode("ascii") + + value = value.encode("ascii") return (bytes(value) + b'\x00' * self.len)[:self.len] def validate(self, frame, value): if value is None: raise TypeError - if PY3: - if not isinstance(value, str): - raise TypeError("%s has to be str" % self.name) - value.encode("ascii") - else: - if not isinstance(value, bytes): - value = value.encode("ascii") + + if not isinstance(value, str): + raise TypeError("%s has to be str" % self.name) + value.encode("ascii") if len(value) == self.len: return value @@ -424,7 +417,7 @@ class BinaryDataSpec(Spec): def write(self, config, frame, value): if isinstance(value, bytes): return value - value = text_type(value).encode("ascii") + value = str(value).encode("ascii") return value def validate(self, frame, value): @@ -432,10 +425,10 @@ class BinaryDataSpec(Spec): raise TypeError if isinstance(value, bytes): return value - elif PY3: + else: raise TypeError("%s has to be bytes" % self.name) - value = text_type(value).encode("ascii") + value = str(value).encode("ascii") return value @@ -493,7 +486,7 @@ class EncodedTextSpec(Spec): raise SpecError(e) def validate(self, frame, value): - return text_type(value) + return str(value) class MultiSpec(Spec): @@ -522,26 +515,26 @@ class MultiSpec(Spec): data.append(self.specs[0].write(config, frame, v)) else: for record in value: - for v, s in izip(record, self.specs): + for v, s in zip(record, self.specs): data.append(s.write(config, frame, v)) return b''.join(data) def validate(self, frame, value): - if self.sep and isinstance(value, string_types): + if self.sep and isinstance(value, str): value = value.split(self.sep) if isinstance(value, list): if len(self.specs) == 1: return [self.specs[0].validate(frame, v) for v in value] else: return [ - [s.validate(frame, v) for (v, s) in izip(val, self.specs)] + [s.validate(frame, v) for (v, s) in zip(val, self.specs)] for val in value] raise ValueError('Invalid MultiSpec data: %r' % value) def _validate23(self, frame, value, **kwargs): if len(self.specs) != 1: return [[s._validate23(frame, v, **kwargs) - for (v, s) in izip(val, self.specs)] + for (v, s) in zip(val, self.specs)] for val in value] spec = self.specs[0] @@ -582,7 +575,7 @@ class Latin1TextSpec(Spec): return value.encode('latin1') + b'\x00' def validate(self, frame, value): - return text_type(value) + return str(value) class ID3FramesSpec(Spec): @@ -632,7 +625,7 @@ class Latin1TextListSpec(Spec): def read(self, header, frame, data): count, data = self._bspec.read(header, frame, data) entries = [] - for i in xrange(count): + for i in range(count): entry, data = self._lspec.read(header, frame, data) entries.append(entry) return entries, data @@ -647,7 +640,6 @@ class Latin1TextListSpec(Spec): return [self._lspec.validate(frame, v) for v in value] -@swap_to_string @total_ordering class ID3TimeStamp(object): """A time stamp in ID3v2 format. @@ -665,10 +657,8 @@ class ID3TimeStamp(object): def __init__(self, text): if isinstance(text, ID3TimeStamp): text = text.text - elif not isinstance(text, text_type): - if PY3: - raise TypeError("not a str") - text = text.decode("utf-8") + elif not isinstance(text, str): + raise TypeError("not a str") self.text = text @@ -736,7 +726,7 @@ class TimeStampSpec(EncodedTextSpec): class ChannelSpec(ByteSpec): (OTHER, MASTER, FRONTRIGHT, FRONTLEFT, BACKRIGHT, BACKLEFT, FRONTCENTRE, - BACKCENTRE, SUBWOOFER) = xrange(9) + BACKCENTRE, SUBWOOFER) = range(9) class VolumeAdjustmentSpec(Spec): @@ -771,7 +761,7 @@ class VolumePeakSpec(Spec): if vol_bytes + 1 > len(data): raise SpecError("not enough frame data") shift = ((8 - (bits & 7)) & 7) + (4 - vol_bytes) * 8 - for i in xrange(1, vol_bytes + 1): + for i in range(1, vol_bytes + 1): peak *= 256 peak += data_array[i] peak *= 2 ** shift diff --git a/libs/common/mutagen/id3/_tags.py b/libs/common/mutagen/id3/_tags.py index 99845faa..c49dba29 100644 --- a/libs/common/mutagen/id3/_tags.py +++ b/libs/common/mutagen/id3/_tags.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2005 Michael Urman # Copyright 2016 Christoph Reiter # @@ -7,11 +6,12 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. +import re import struct +from itertools import zip_longest from mutagen._tags import Tags from mutagen._util import DictProxy, convert_error, read_full -from mutagen._compat import PY3, text_type, itervalues from ._util import BitPaddedInt, unsynch, ID3JunkFrameError, \ ID3EncryptionUnsupportedError, is_valid_frame_id, error, \ @@ -82,10 +82,7 @@ class ID3Header(object): if self.f_extended: extsize_data = read_full(fileobj, 4) - if PY3: - frame_id = extsize_data.decode("ascii", "replace") - else: - frame_id = extsize_data + frame_id = extsize_data.decode("ascii", "replace") if frame_id in Frames: # Some tagger sets the extended header flag but @@ -131,11 +128,10 @@ def determine_bpi(data, frames, EMPTY=b"\x00" * 10): name, size, flags = struct.unpack('>4sLH', part) size = BitPaddedInt(size) o += 10 + size - if PY3: - try: - name = name.decode("ascii") - except UnicodeDecodeError: - continue + try: + name = name.decode("ascii") + except UnicodeDecodeError: + continue if name in frames: asbpi += 1 else: @@ -151,11 +147,10 @@ def determine_bpi(data, frames, EMPTY=b"\x00" * 10): break name, size, flags = struct.unpack('>4sLH', part) o += 10 + size - if PY3: - try: - name = name.decode("ascii") - except UnicodeDecodeError: - continue + try: + name = name.decode("ascii") + except UnicodeDecodeError: + continue if name in frames: asint += 1 else: @@ -191,7 +186,7 @@ class ID3Tags(DictProxy, Tags): order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"] framedata = [ - (f, save_frame(f, config=config)) for f in itervalues(self)] + (f, save_frame(f, config=config)) for f in self.values()] def get_prio(frame): try: @@ -243,7 +238,7 @@ class ID3Tags(DictProxy, Tags): Args: key (text): key for frames to delete - values (List[`Frame`]): frames to add + values (list[Frame]): frames to add """ self.delall(key) @@ -369,24 +364,23 @@ class ID3Tags(DictProxy, Tags): self.__update_common() # TDAT, TYER, and TIME have been turned into TDRC. - try: - date = text_type(self.get("TYER", "")) - if date.strip(u"\x00"): - self.pop("TYER") - dat = text_type(self.get("TDAT", "")) - if dat.strip("\x00"): - self.pop("TDAT") - date = "%s-%s-%s" % (date, dat[2:], dat[:2]) - time = text_type(self.get("TIME", "")) - if time.strip("\x00"): - self.pop("TIME") - date += "T%s:%s:00" % (time[:2], time[2:]) - if "TDRC" not in self: - self.add(TDRC(encoding=0, text=date)) - except UnicodeDecodeError: - # Old ID3 tags have *lots* of Unicode problems, so if TYER - # is bad, just chuck the frames. - pass + timestamps = [] + old_frames = [self.pop(n, []) for n in ["TYER", "TDAT", "TIME"]] + for y, d, t in zip_longest(*old_frames, fillvalue=u""): + ym = re.match(r"([0-9]+)\Z", y) + dm = re.match(r"([0-9]{2})([0-9]{2})\Z", d) + tm = re.match(r"([0-9]{2})([0-9]{2})\Z", t) + timestamp = "" + if ym: + timestamp += u"%s" % ym.groups() + if dm: + timestamp += u"-%s-%s" % dm.groups()[::-1] + if tm: + timestamp += u"T%s:%s:00" % tm.groups() + if timestamp: + timestamps.append(timestamp) + if timestamps and "TDRC" not in self: + self.add(TDRC(encoding=0, text=timestamps)) # TORY can be the first part of a TDOR. if "TORY" in self: @@ -533,8 +527,7 @@ def save_frame(frame, name=None, config=None): frame_name = name else: frame_name = type(frame).__name__ - if PY3: - frame_name = frame_name.encode("ascii") + frame_name = frame_name.encode("ascii") header = struct.pack('>4s4sH', frame_name, datasize, flags) return header + framedata @@ -575,11 +568,10 @@ def read_frames(id3, data, frames): if size == 0: continue # drop empty frames - if PY3: - try: - name = name.decode('ascii') - except UnicodeDecodeError: - continue + try: + name = name.decode('ascii') + except UnicodeDecodeError: + continue try: # someone writes 2.3 frames with 2.2 names @@ -614,11 +606,10 @@ def read_frames(id3, data, frames): if size == 0: continue # drop empty frames - if PY3: - try: - name = name.decode('ascii') - except UnicodeDecodeError: - continue + try: + name = name.decode('ascii') + except UnicodeDecodeError: + continue try: tag = frames[name] diff --git a/libs/common/mutagen/id3/_util.py b/libs/common/mutagen/id3/_util.py index 93bb264e..b028d9c6 100644 --- a/libs/common/mutagen/id3/_util.py +++ b/libs/common/mutagen/id3/_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005 Michael Urman # 2013 Christoph Reiter # 2014 Ben Ockmore @@ -8,7 +7,6 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -from mutagen._compat import long_, integer_types, PY3 from mutagen._util import MutagenError @@ -110,7 +108,7 @@ class _BitPaddedMixin(object): mask = (((1 << (8 - bits)) - 1) << bits) - if isinstance(value, integer_types): + if isinstance(value, int): while value: if value & mask: return False @@ -133,7 +131,7 @@ class BitPaddedInt(int, _BitPaddedMixin): numeric_value = 0 shift = 0 - if isinstance(value, integer_types): + if isinstance(value, int): if value < 0: raise ValueError while value: @@ -149,21 +147,12 @@ class BitPaddedInt(int, _BitPaddedMixin): else: raise TypeError - if isinstance(numeric_value, int): - self = int.__new__(BitPaddedInt, numeric_value) - else: - self = long_.__new__(BitPaddedLong, numeric_value) + self = int.__new__(BitPaddedInt, numeric_value) self.bits = bits self.bigendian = bigendian return self -if PY3: - BitPaddedLong = BitPaddedInt -else: - class BitPaddedLong(long_, _BitPaddedMixin): - pass - class ID3BadUnsynchData(error, ValueError): """Deprecated""" diff --git a/libs/common/mutagen/m4a.py b/libs/common/mutagen/m4a.py index c7583f8e..eea2136b 100644 --- a/libs/common/mutagen/m4a.py +++ b/libs/common/mutagen/m4a.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify diff --git a/libs/common/mutagen/monkeysaudio.py b/libs/common/mutagen/monkeysaudio.py index 82bfcd24..30cf78c0 100644 --- a/libs/common/mutagen/monkeysaudio.py +++ b/libs/common/mutagen/monkeysaudio.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Lukas Lalinsky # # This program is free software; you can redistribute it and/or modify @@ -18,10 +17,9 @@ __all__ = ["MonkeysAudio", "Open", "delete"] import struct -from ._compat import endswith from mutagen import StreamInfo from mutagen.apev2 import APEv2File, error, delete -from mutagen._util import cdata, convert_error +from mutagen._util import cdata, convert_error, endswith class MonkeysAudioHeaderError(error): diff --git a/libs/common/mutagen/mp3/__init__.py b/libs/common/mutagen/mp3/__init__.py index 8ce70e35..1c9b7e5c 100644 --- a/libs/common/mutagen/mp3/__init__.py +++ b/libs/common/mutagen/mp3/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -12,8 +11,7 @@ import struct from mutagen import StreamInfo from mutagen._util import MutagenError, enum, BitReader, BitReaderError, \ - convert_error, intround -from mutagen._compat import endswith, xrange + convert_error, intround, endswith from mutagen.id3 import ID3FileType, delete from mutagen.id3._util import BitPaddedInt @@ -75,27 +73,27 @@ def _guess_xing_bitrate_mode(xing): # Mode values. -STEREO, JOINTSTEREO, DUALCHANNEL, MONO = xrange(4) +STEREO, JOINTSTEREO, DUALCHANNEL, MONO = range(4) class MPEGFrame(object): # Map (version, layer) tuples to bitrates. __BITRATE = { - (1, 1): [0, 32, 64, 96, 128, 160, 192, 224, - 256, 288, 320, 352, 384, 416, 448], - (1, 2): [0, 32, 48, 56, 64, 80, 96, 112, 128, - 160, 192, 224, 256, 320, 384], - (1, 3): [0, 32, 40, 48, 56, 64, 80, 96, 112, - 128, 160, 192, 224, 256, 320], - (2, 1): [0, 32, 48, 56, 64, 80, 96, 112, 128, - 144, 160, 176, 192, 224, 256], - (2, 2): [0, 8, 16, 24, 32, 40, 48, 56, 64, - 80, 96, 112, 128, 144, 160], + (1., 1): [0, 32, 64, 96, 128, 160, 192, 224, + 256, 288, 320, 352, 384, 416, 448], + (1., 2): [0, 32, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384], + (1., 3): [0, 32, 40, 48, 56, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 320], + (2., 1): [0, 32, 48, 56, 64, 80, 96, 112, 128, + 144, 160, 176, 192, 224, 256], + (2., 2): [0, 8, 16, 24, 32, 40, 48, 56, 64, + 80, 96, 112, 128, 144, 160], } __BITRATE[(2, 3)] = __BITRATE[(2, 2)] - for i in xrange(1, 4): + for i in range(1, 4): __BITRATE[(2.5, i)] = __BITRATE[(2, i)] # Map version to sample rates. @@ -306,7 +304,7 @@ class MPEGInfo(StreamInfo): bitrate (`int`): audio bitrate, in bits per second. In case :attr:`bitrate_mode` is :attr:`BitrateMode.UNKNOWN` the bitrate is guessed based on the first frame. - sample_rate (`int`) audio sample rate, in Hz + sample_rate (`int`): audio sample rate, in Hz encoder_info (`mutagen.text`): a string containing encoder name and possibly version. In case a lame tag is present this will start with ``"LAME "``, if unknown it is empty, otherwise the @@ -357,7 +355,7 @@ class MPEGInfo(StreamInfo): # find a sync in the first 1024K, give up after some invalid syncs max_read = 1024 * 1024 - max_syncs = 1000 + max_syncs = 1500 enough_frames = 4 min_frames = 2 @@ -370,7 +368,7 @@ class MPEGInfo(StreamInfo): if max_syncs <= 0: break - for _ in xrange(enough_frames): + for _ in range(enough_frames): try: frame = MPEGFrame(fileobj) except HeaderNotFoundError: @@ -480,4 +478,4 @@ class EasyMP3(MP3): """ from mutagen.easyid3 import EasyID3 as ID3 - ID3 = ID3 + ID3 = ID3 # type: ignore diff --git a/libs/common/mutagen/mp3/_util.py b/libs/common/mutagen/mp3/_util.py index fd1b5ca3..b67ab6aa 100644 --- a/libs/common/mutagen/mp3/_util.py +++ b/libs/common/mutagen/mp3/_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2015 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -13,9 +12,10 @@ http://wiki.hydrogenaud.io/index.php?title=MP3 from __future__ import division from functools import partial +from io import BytesIO +from typing import List -from mutagen._util import cdata, BitReader -from mutagen._compat import xrange, iterbytes, cBytesIO +from mutagen._util import cdata, BitReader, iterbytes class LAMEError(Exception): @@ -109,7 +109,7 @@ class LAMEHeader(object): raise LAMEError("Not enough data") # extended lame header - r = BitReader(cBytesIO(payload)) + r = BitReader(BytesIO(payload)) revision = r.bits(4) if revision != 0: raise LAMEError("unsupported header revision %d" % revision) @@ -356,7 +356,7 @@ class XingHeader(object): bytes = -1 """Number of bytes, -1 if unknown""" - toc = [] + toc: List[int] = [] """List of 100 file offsets in percent encoded as 0-255. E.g. entry 50 contains the file offset in percent at 50% play time. Empty if unknown. @@ -474,7 +474,7 @@ class VBRIHeader(object): toc_frames = 0 """Number of frames per table entry""" - toc = [] + toc: List[int] = [] """TOC""" def __init__(self, fileobj): @@ -515,7 +515,7 @@ class VBRIHeader(object): else: raise VBRIHeaderError("Invalid TOC entry size") - self.toc = [unpack(i)[0] for i in xrange(0, toc_size, toc_entry_size)] + self.toc = [unpack(i)[0] for i in range(0, toc_size, toc_entry_size)] @classmethod def get_offset(cls, info): diff --git a/libs/common/mutagen/mp4/__init__.py b/libs/common/mutagen/mp4/__init__.py index 75ec5769..87a38f66 100644 --- a/libs/common/mutagen/mp4/__init__.py +++ b/libs/common/mutagen/mp4/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -25,13 +24,15 @@ were all consulted. import struct import sys +from io import BytesIO +from collections.abc import Sequence +from datetime import timedelta from mutagen import FileType, Tags, StreamInfo, PaddingInfo from mutagen._constants import GENRES from mutagen._util import cdata, insert_bytes, DictProxy, MutagenError, \ - hashable, enum, get_size, resize_bytes, loadfile, convert_error -from mutagen._compat import (reraise, PY2, string_types, text_type, chr_, - iteritems, PY3, cBytesIO, izip, xrange) + hashable, enum, get_size, resize_bytes, loadfile, convert_error, bchr, \ + reraise from ._atom import Atoms, Atom, AtomError from ._util import parse_full_atom from ._as_entry import AudioSampleEntry, ASEntryError @@ -205,14 +206,10 @@ class MP4FreeForm(bytes): def _name2key(name): - if PY2: - return name return name.decode("latin-1") def _key2name(key): - if PY2: - return key return key.encode("latin-1") @@ -246,7 +243,7 @@ def _item_sort_key(key, value): "\xa9gen", "gnre", "trkn", "disk", "\xa9day", "cpil", "pgap", "pcst", "tmpo", "\xa9too", "----", "covr", "\xa9lyr"] - order = dict(izip(order, xrange(len(order)))) + order = dict(zip(order, range(len(order)))) last = len(order) # If there's no key-based way to distinguish, order by length. # If there's still no way, go by string comparison on the @@ -311,6 +308,7 @@ class MP4Tags(DictProxy, Tags): * '\\xa9mvi' -- Movement Index * 'shwm' -- work/movement * 'stik' -- Media Kind + * 'hdvd' -- HD Video * 'rtng' -- Content Rating * 'tves' -- TV Episode * 'tvsn' -- TV Season @@ -366,8 +364,7 @@ class MP4Tags(DictProxy, Tags): self.__parse_text(atom, data, implicit=False) except MP4MetadataError: # parsing failed, save them so we can write them back - key = _name2key(atom.name) - self._failed_atoms.setdefault(key, []).append(data) + self._failed_atoms.setdefault(_name2key(atom.name), []).append(data) def __setitem__(self, key, value): if not isinstance(key, str): @@ -392,7 +389,7 @@ class MP4Tags(DictProxy, Tags): @convert_error(IOError, error) @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): values = [] items = sorted(self.items(), key=lambda kv: _item_sort_key(*kv)) @@ -402,7 +399,7 @@ class MP4Tags(DictProxy, Tags): except (TypeError, ValueError) as s: reraise(MP4MetadataValueError, s, sys.exc_info()[2]) - for key, failed in iteritems(self._failed_atoms): + for key, failed in self._failed_atoms.items(): # don't write atoms back if we have added a new one with # the same name, this excludes freeform which can have # multiple atoms with the same key (most parsers seem to be able @@ -562,6 +559,9 @@ class MP4Tags(DictProxy, Tags): if len(head) != 12: raise MP4MetadataError("truncated atom % r" % atom.name) length, name = struct.unpack(">I4s", head[:8]) + if length < 1: + raise MP4MetadataError( + "atom %r has a length of zero" % atom.name) version = ord(head[8:9]) flags = struct.unpack(">I", b"\x00" + head[9:12])[0] if name != b"data": @@ -601,7 +601,9 @@ class MP4Tags(DictProxy, Tags): if atom_name != b"data": raise MP4MetadataError( "unexpected atom %r inside %r" % (atom_name, atom.name)) - + if length < 1: + raise MP4MetadataError( + "atom %r has a length of zero" % atom.name) version = ord(data[pos + 8:pos + 8 + 1]) flags = struct.unpack(">I", b"\x00" + data[pos + 9:pos + 12])[0] value.append(MP4FreeForm(data[pos + 16:pos + length], @@ -746,7 +748,7 @@ class MP4Tags(DictProxy, Tags): def __render_bool(self, key, value): return self.__render_data( - key, 0, AtomDataType.INTEGER, [chr_(bool(value))]) + key, 0, AtomDataType.INTEGER, [bchr(bool(value))]) def __parse_cover(self, atom, data): values = [] @@ -760,6 +762,9 @@ class MP4Tags(DictProxy, Tags): continue raise MP4MetadataError( "unexpected atom %r inside 'covr'" % name) + if length < 1: + raise MP4MetadataError( + "atom %r has a length of zero" % atom.name) if imageformat not in (MP4Cover.FORMAT_JPEG, MP4Cover.FORMAT_PNG): # Sometimes AtomDataType.IMPLICIT or simply wrong. # In all cases it was jpeg, so default to it @@ -807,18 +812,14 @@ class MP4Tags(DictProxy, Tags): self.__add(key, values) def __render_text(self, key, value, flags=AtomDataType.UTF8): - if isinstance(value, string_types): + if isinstance(value, str): value = [value] encoded = [] for v in value: - if not isinstance(v, text_type): - if PY3: - raise TypeError("%r not str" % v) - try: - v = v.decode("utf-8") - except (AttributeError, UnicodeDecodeError) as e: - raise TypeError(e) + if not isinstance(v, str): + raise TypeError("%r not str" % v) + encoded.append(v.encode("utf-8")) return self.__render_data(key, 0, flags, encoded) @@ -852,6 +853,7 @@ class MP4Tags(DictProxy, Tags): b"pcst": (__parse_bool, __render_bool), b"shwm": (__parse_integer, __render_integer, 1), b"stik": (__parse_integer, __render_integer, 1), + b"hdvd": (__parse_integer, __render_integer, 1), b"rtng": (__parse_integer, __render_integer, 1), b"covr": (__parse_cover, __render_cover), b"purl": (__parse_text, __render_text), @@ -869,14 +871,14 @@ class MP4Tags(DictProxy, Tags): def pprint(self): def to_line(key, value): - assert isinstance(key, text_type) - if isinstance(value, text_type): + assert isinstance(key, str) + if isinstance(value, str): return u"%s=%s" % (key, value) return u"%s=%r" % (key, value) values = [] - for key, value in sorted(iteritems(self)): - if not isinstance(key, text_type): + for key, value in sorted(self.items()): + if not isinstance(key, str): key = key.decode("latin-1") if key == "covr": values.append(u"%s=%s" % (key, u", ".join( @@ -889,6 +891,123 @@ class MP4Tags(DictProxy, Tags): return u"\n".join(values) +class Chapter(object): + """Chapter() + + Chapter information container + """ + def __init__(self, start, title): + self.start = start + self.title = title + + +class MP4Chapters(Sequence): + """MP4Chapters() + + MPEG-4 Chapter information. + + Supports the 'moov.udta.chpl' box. + + A sequence of Chapter objects with the following members: + start (`float`): position from the start of the file in seconds + title (`str`): title of the chapter + + """ + + def __init__(self, *args, **kwargs): + self._timescale = None + self._duration = None + self._chapters = [] + super(MP4Chapters, self).__init__() + if args or kwargs: + self.load(*args, **kwargs) + + def __len__(self): + return self._chapters.__len__() + + def __getitem__(self, key): + return self._chapters.__getitem__(key) + + def load(self, atoms, fileobj): + try: + mvhd = atoms.path(b"moov", b"mvhd")[-1] + except KeyError as key: + return MP4MetadataError(key) + + self._parse_mvhd(mvhd, fileobj) + + if not self._timescale: + raise MP4MetadataError("Unable to get timescale") + + try: + chpl = atoms.path(b"moov", b"udta", b"chpl")[-1] + except KeyError as key: + return MP4MetadataError(key) + + self._parse_chpl(chpl, fileobj) + + @classmethod + def _can_load(cls, atoms): + return b"moov.udta.chpl" in atoms and b"moov.mvhd" in atoms + + def _parse_mvhd(self, atom, fileobj): + assert atom.name == b"mvhd" + + ok, data = atom.read(fileobj) + if not ok: + raise MP4StreamInfoError("Invalid mvhd") + + version = data[0] + + pos = 4 + if version == 0: + pos += 8 # created, modified + + self._timescale = struct.unpack(">l", data[pos:pos + 4])[0] + pos += 4 + + self._duration = struct.unpack(">l", data[pos:pos + 4])[0] + pos += 4 + elif version == 1: + pos += 16 # created, modified + + self._timescale = struct.unpack(">l", data[pos:pos + 4])[0] + pos += 4 + + self._duration = struct.unpack(">q", data[pos:pos + 8])[0] + pos += 8 + + def _parse_chpl(self, atom, fileobj): + assert atom.name == b"chpl" + + ok, data = atom.read(fileobj) + if not ok: + raise MP4StreamInfoError("Invalid atom") + + chapters = data[8] + + pos = 9 + for i in range(chapters): + start = struct.unpack(">Q", data[pos:pos + 8])[0] / 10000 + pos += 8 + + title_len = data[pos] + pos += 1 + + try: + title = data[pos:pos + title_len].decode() + except UnicodeDecodeError as e: + raise MP4MetadataError("chapter %d title: %s" % (i, e)) + pos += title_len + + self._chapters.append(Chapter(start / self._timescale, title)) + + def pprint(self): + chapters = ["%s %s" % (timedelta(seconds=chapter.start), chapter.title) + for chapter in self._chapters] + return "chapters=%s" % '\n '.join(chapters) + + class MP4Info(StreamInfo): """MP4Info() @@ -1004,7 +1123,7 @@ class MP4Info(StreamInfo): return # look at the first entry if there is one - entry_fileobj = cBytesIO(data[offset:]) + entry_fileobj = BytesIO(data[offset:]) try: entry_atom = Atom(entry_fileobj) except AtomError as e: @@ -1044,6 +1163,7 @@ class MP4(FileType): """ MP4Tags = MP4Tags + MP4Chapters = MP4Chapters _mimes = ["audio/mp4", "audio/x-m4a", "audio/mpeg4", "audio/aac"] @@ -1076,6 +1196,16 @@ class MP4(FileType): except Exception as err: reraise(MP4MetadataError, err, sys.exc_info()[2]) + if not MP4Chapters._can_load(atoms): + self.chapters = None + else: + try: + self.chapters = self.MP4Chapters(atoms, fileobj) + except error: + raise + except Exception as err: + reraise(MP4MetadataError, err, sys.exc_info()[2]) + @property def _padding(self): if self.tags is None: @@ -1088,6 +1218,28 @@ class MP4(FileType): super(MP4, self).save(*args, **kwargs) + def pprint(self): + """ + Returns: + text: stream information, comment key=value pairs and chapters. + """ + stream = "%s (%s)" % (self.info.pprint(), self.mime[0]) + try: + tags = self.tags.pprint() + except AttributeError: + pass + else: + stream += ((tags and "\n" + tags) or "") + + try: + chapters = self.chapters.pprint() + except AttributeError: + pass + else: + stream += "\n" + chapters + + return stream + def add_tags(self): if self.tags is None: self.tags = self.MP4Tags() diff --git a/libs/common/mutagen/mp4/_as_entry.py b/libs/common/mutagen/mp4/_as_entry.py index 15b7e6bc..e5013c42 100644 --- a/libs/common/mutagen/mp4/_as_entry.py +++ b/libs/common/mutagen/mp4/_as_entry.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2014 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -6,10 +5,10 @@ # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -from mutagen._compat import cBytesIO, xrange +from io import BytesIO + from mutagen.aac import ProgramConfigElement from mutagen._util import BitReader, BitReaderError, cdata -from mutagen._compat import text_type from ._util import parse_full_atom from ._atom import Atom, AtomError @@ -47,7 +46,7 @@ class AudioSampleEntry(object): if not ok: raise ASEntryError("too short %r atom" % atom.name) - fileobj = cBytesIO(data) + fileobj = BytesIO(data) r = BitReader(fileobj) try: @@ -93,7 +92,7 @@ class AudioSampleEntry(object): ok, data = atom.read(fileobj) if not ok: raise ASEntryError("truncated %s atom" % atom.name) - fileobj = cBytesIO(data) + fileobj = BytesIO(data) r = BitReader(fileobj) # sample_rate in AudioSampleEntry covers values in @@ -134,7 +133,7 @@ class AudioSampleEntry(object): if version != 0: raise ASEntryError("Unsupported version %d" % version) - fileobj = cBytesIO(data) + fileobj = BytesIO(data) r = BitReader(fileobj) try: @@ -168,7 +167,7 @@ class AudioSampleEntry(object): if version != 0: raise ASEntryError("Unsupported version %d" % version) - fileobj = cBytesIO(data) + fileobj = BytesIO(data) r = BitReader(fileobj) try: @@ -204,14 +203,14 @@ class DescriptorError(Exception): class BaseDescriptor(object): - TAG = None + TAG: int @classmethod def _parse_desc_length_file(cls, fileobj): """May raise ValueError""" value = 0 - for i in xrange(4): + for i in range(4): try: b = cdata.uint8(fileobj.read(1)) except cdata.error as e: @@ -239,9 +238,13 @@ class BaseDescriptor(object): pos = fileobj.tell() instance = cls(fileobj, length) left = length - (fileobj.tell() - pos) - if left < 0: - raise DescriptorError("descriptor parsing read too much data") - fileobj.seek(left, 1) + if left > 0: + fileobj.seek(left, 1) + else: + # XXX: In case the instance length is shorted than the content + # assume the size is wrong and just continue parsing + # https://github.com/quodlibet/mutagen/issues/444 + pass return instance @@ -371,7 +374,7 @@ class DecoderSpecificInfo(BaseDescriptor): name += "+SBR" if self.psPresentFlag == 1: name += "+PS" - return text_type(name) + return str(name) @property def sample_rate(self): diff --git a/libs/common/mutagen/mp4/_atom.py b/libs/common/mutagen/mp4/_atom.py index cd43a1fe..e1bf1535 100644 --- a/libs/common/mutagen/mp4/_atom.py +++ b/libs/common/mutagen/mp4/_atom.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -8,7 +7,6 @@ import struct -from mutagen._compat import PY2 from mutagen._util import convert_error # This is not an exhaustive list of container atoms, but just the @@ -180,12 +178,8 @@ class Atoms(object): specifying the complete path ('moov.udta'). """ - if PY2: - if isinstance(names, basestring): - names = names.split(b".") - else: - if isinstance(names, bytes): - names = names.split(b".") + if isinstance(names, bytes): + names = names.split(b".") for child in self.atoms: if child.name == names[0]: diff --git a/libs/common/mutagen/mp4/_util.py b/libs/common/mutagen/mp4/_util.py index 43d81c82..b8e208a1 100644 --- a/libs/common/mutagen/mp4/_util.py +++ b/libs/common/mutagen/mp4/_util.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2014 Christoph Reiter # # This program is free software; you can redistribute it and/or modify diff --git a/libs/common/mutagen/musepack.py b/libs/common/mutagen/musepack.py index c966d939..944de6d5 100644 --- a/libs/common/mutagen/musepack.py +++ b/libs/common/mutagen/musepack.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Lukas Lalinsky # Copyright (C) 2012 Christoph Reiter # @@ -19,11 +18,10 @@ __all__ = ["Musepack", "Open", "delete"] import struct -from ._compat import endswith, xrange from mutagen import StreamInfo from mutagen.apev2 import APEv2File, error, delete from mutagen.id3._util import BitPaddedInt -from mutagen._util import cdata, convert_error, intround +from mutagen._util import cdata, convert_error, intround, endswith class MusepackHeaderError(error): @@ -44,7 +42,7 @@ def _parse_sv8_int(fileobj, limit=9): """ num = 0 - for i in xrange(limit): + for i in range(limit): c = fileobj.read(1) if len(c) != 1: raise EOFError @@ -143,9 +141,13 @@ class MusepackInfo(StreamInfo): # packets can be at maximum data_size big and are padded with zeros if frame_type == b"SH": + if frame_type not in mandatory_packets: + raise MusepackHeaderError("Duplicate SH packet") mandatory_packets.remove(frame_type) self.__parse_stream_header(fileobj, data_size) elif frame_type == b"RG": + if frame_type not in mandatory_packets: + raise MusepackHeaderError("Duplicate RG packet") mandatory_packets.remove(frame_type) self.__parse_replaygain_packet(fileobj, data_size) else: @@ -184,9 +186,13 @@ class MusepackInfo(StreamInfo): remaining_size -= l1 + l2 data = fileobj.read(remaining_size) - if len(data) != remaining_size: + if len(data) != remaining_size or len(data) < 2: raise MusepackHeaderError("SH packet ended unexpectedly.") - self.sample_rate = RATES[bytearray(data)[0] >> 5] + rate_index = (bytearray(data)[0] >> 5) + try: + self.sample_rate = RATES[rate_index] + except IndexError: + raise MusepackHeaderError("Invalid sample rate") self.channels = (bytearray(data)[1] >> 4) + 1 def __parse_replaygain_packet(self, fileobj, data_size): diff --git a/libs/common/mutagen/ogg.py b/libs/common/mutagen/ogg.py index 22d7442c..659e6cd4 100644 --- a/libs/common/mutagen/ogg.py +++ b/libs/common/mutagen/ogg.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -19,10 +18,14 @@ http://www.xiph.org/ogg/doc/rfc3533.txt. import struct import sys import zlib +from io import BytesIO +from typing import Type from mutagen import FileType -from mutagen._util import cdata, resize_bytes, MutagenError, loadfile, seek_end -from ._compat import cBytesIO, reraise, chr_, izip, xrange +from mutagen._util import cdata, resize_bytes, MutagenError, loadfile, \ + seek_end, bchr, reraise +from mutagen._file import StreamInfo +from mutagen._tags import Tags class error(MutagenError): @@ -37,7 +40,7 @@ class OggPage(object): A page is a header of 26 bytes, followed by the length of the data, followed by the data. - The constructor is givin a file-like object pointing to the start + The constructor is given a file-like object pointing to the start of an Ogg page. After the constructor is finished it is pointing to the start of the next page. @@ -50,7 +53,7 @@ class OggPage(object): offset (`int` or `None`): offset this page was read from (default None) complete (`bool`): if the last packet on this page is complete (default True) - packets (List[`bytes`]): list of raw packet data (default []) + packets (list[bytes]): list of raw packet data (default []) Note that if 'complete' is false, the next page's 'continued' property must be true (so set both when constructing pages). @@ -145,11 +148,11 @@ class OggPage(object): lacing_data = [] for datum in self.packets: quot, rem = divmod(len(datum), 255) - lacing_data.append(b"\xff" * quot + chr_(rem)) + lacing_data.append(b"\xff" * quot + bchr(rem)) lacing_data = b"".join(lacing_data) if not self.complete and lacing_data.endswith(b"\x00"): lacing_data = lacing_data[:-1] - data.append(chr_(len(lacing_data))) + data.append(bchr(len(lacing_data))) data.append(lacing_data) data.extend(self.packets) data = b"".join(data) @@ -210,13 +213,13 @@ class OggPage(object): to logical stream 'serial'. Other pages will be ignored. fileobj must point to the start of a valid Ogg page; any - occuring after it and part of the specified logical stream + occurring after it and part of the specified logical stream will be numbered. No adjustment will be made to the data in the pages nor the granule position; only the page number, and so also the CRC. If an error occurs (e.g. non-Ogg data is found), fileobj will - be left pointing to the place in the stream the error occured, + be left pointing to the place in the stream the error occurred, but the invalid data will be left intact (since this function does not change the total file size). """ @@ -267,11 +270,12 @@ class OggPage(object): else: sequence += 1 - if page.continued: - packets[-1].append(page.packets[0]) - else: - packets.append([page.packets[0]]) - packets.extend([p] for p in page.packets[1:]) + if page.packets: + if page.continued: + packets[-1].append(page.packets[0]) + else: + packets.append([page.packets[0]]) + packets.extend([p] for p in page.packets[1:]) return [b"".join(p) for p in packets] @@ -387,8 +391,8 @@ class OggPage(object): # Number the new pages starting from the first old page. first = old_pages[0].sequence - for page, seq in izip(new_pages, - xrange(first, first + len(new_pages))): + for page, seq in zip(new_pages, + range(first, first + len(new_pages))): page.sequence = seq page.serial = old_pages[0].serial @@ -416,7 +420,7 @@ class OggPage(object): offset_adjust = 0 new_data_end = None assert len(old_pages) == len(new_data) - for old_page, data in izip(old_pages, new_data): + for old_page, data in zip(old_pages, new_data): offset = old_page.offset + offset_adjust data_size = len(data) resize_bytes(fileobj, old_page.size, data_size, offset) @@ -460,7 +464,7 @@ class OggPage(object): index = data.rindex(b"OggS") except ValueError: raise error("unable to find final Ogg header") - bytesobj = cBytesIO(data[index:]) + bytesobj = BytesIO(data[index:]) def is_valid(page): return not finishing or page.position != -1 @@ -506,9 +510,9 @@ class OggFileType(FileType): filething (filething) """ - _Info = None - _Tags = None - _Error = None + _Info: Type[StreamInfo] + _Tags: Type[Tags] + _Error: Type[error] _mimes = ["application/ogg", "application/x-ogg"] @loadfile() @@ -535,7 +539,7 @@ class OggFileType(FileType): raise self._Error("no appropriate stream found") @loadfile(writable=True) - def delete(self, filething): + def delete(self, filething=None): """delete(filething=None) Remove tags from a file. @@ -567,7 +571,7 @@ class OggFileType(FileType): raise self._Error @loadfile(writable=True) - def save(self, filething, padding=None): + def save(self, filething=None, padding=None): """save(filething=None, padding=None) Save a tag to a file. diff --git a/libs/common/mutagen/oggflac.py b/libs/common/mutagen/oggflac.py index bc073094..9a4ce3e7 100644 --- a/libs/common/mutagen/oggflac.py +++ b/libs/common/mutagen/oggflac.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -18,8 +17,7 @@ http://flac.sourceforge.net/ogg_mapping.html. __all__ = ["OggFLAC", "Open", "delete"] import struct - -from ._compat import cBytesIO +from io import BytesIO from mutagen import StreamInfo from mutagen.flac import StreamInfo as FLACStreamInfo, error as FLACError @@ -65,7 +63,7 @@ class OggFLACStreamInfo(StreamInfo): self.serial = page.serial # Skip over the block header. - stringobj = cBytesIO(page.packets[0][17:]) + stringobj = BytesIO(page.packets[0][17:]) try: flac_info = FLACStreamInfo(stringobj) @@ -101,7 +99,7 @@ class OggFLACVComment(VCommentDict): if page.serial == info.serial: pages.append(page) complete = page.complete or (len(page.packets) > 1) - comment = cBytesIO(OggPage.to_packets(pages)[0][4:]) + comment = BytesIO(OggPage.to_packets(pages)[0][4:]) super(OggFLACVComment, self).__init__(comment, framing=False) def _inject(self, fileobj, padding_func): diff --git a/libs/common/mutagen/oggopus.py b/libs/common/mutagen/oggopus.py index df9c32e8..486b5853 100644 --- a/libs/common/mutagen/oggopus.py +++ b/libs/common/mutagen/oggopus.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2012, 2013 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -17,9 +16,9 @@ Based on http://tools.ietf.org/html/draft-terriberry-oggopus-01 __all__ = ["OggOpus", "Open", "delete"] import struct +from io import BytesIO from mutagen import StreamInfo -from mutagen._compat import BytesIO from mutagen._util import get_size, loadfile, convert_error from mutagen._tags import PaddingInfo from mutagen._vorbis import VCommentDict diff --git a/libs/common/mutagen/oggspeex.py b/libs/common/mutagen/oggspeex.py index de02a449..a3edb247 100644 --- a/libs/common/mutagen/oggspeex.py +++ b/libs/common/mutagen/oggspeex.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify diff --git a/libs/common/mutagen/oggtheora.py b/libs/common/mutagen/oggtheora.py index a18dfd53..51f0a451 100644 --- a/libs/common/mutagen/oggtheora.py +++ b/libs/common/mutagen/oggtheora.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -50,17 +49,22 @@ class OggTheoraInfo(StreamInfo): def __init__(self, fileobj): page = OggPage(fileobj) - while not page.packets[0].startswith(b"\x80theora"): + while not page.packets or \ + not page.packets[0].startswith(b"\x80theora"): page = OggPage(fileobj) if not page.first: raise OggTheoraHeaderError( "page has ID header, but doesn't start a stream") data = page.packets[0] + if len(data) < 42: + raise OggTheoraHeaderError("Truncated header") vmaj, vmin = struct.unpack("2B", data[7:9]) if (vmaj, vmin) != (3, 2): raise OggTheoraHeaderError( "found Theora version %d.%d != 3.2" % (vmaj, vmin)) fps_num, fps_den = struct.unpack(">2I", data[22:30]) + if not fps_den or not fps_num: + raise OggTheoraHeaderError("FRN or FRD is equal to zero") self.fps = fps_num / float(fps_den) self.bitrate = cdata.uint_be(b"\x00" + data[37:40]) self.granule_shift = (cdata.ushort_be(data[40:42]) >> 5) & 0x1F @@ -73,6 +77,7 @@ class OggTheoraInfo(StreamInfo): position = page.position mask = (1 << self.granule_shift) - 1 frames = (position >> self.granule_shift) + (position & mask) + assert self.fps self.length = frames / float(self.fps) def pprint(self): @@ -91,7 +96,10 @@ class OggTheoraCommentDict(VCommentDict): if page.serial == info.serial: pages.append(page) complete = page.complete or (len(page.packets) > 1) - data = OggPage.to_packets(pages)[0][7:] + packets = OggPage.to_packets(pages) + if not packets: + raise error("Missing metadata packet") + data = packets[0][7:] super(OggTheoraCommentDict, self).__init__(data, framing=False) self._padding = len(data) - self._size @@ -100,7 +108,8 @@ class OggTheoraCommentDict(VCommentDict): fileobj.seek(0) page = OggPage(fileobj) - while not page.packets[0].startswith(b"\x81theora"): + while not page.packets or \ + not page.packets[0].startswith(b"\x81theora"): page = OggPage(fileobj) old_pages = [page] diff --git a/libs/common/mutagen/oggvorbis.py b/libs/common/mutagen/oggvorbis.py index e3292ae3..c1c90732 100644 --- a/libs/common/mutagen/oggvorbis.py +++ b/libs/common/mutagen/oggvorbis.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -43,7 +42,7 @@ class OggVorbisInfo(StreamInfo): length (`float`): File length in seconds, as a float channels (`int`): Number of channels bitrate (`int`): Nominal ('average') bitrate in bits per second - sample_Rate (`int`): Sample rate in Hz + sample_rate (`int`): Sample rate in Hz """ @@ -56,13 +55,20 @@ class OggVorbisInfo(StreamInfo): """Raises ogg.error, IOError""" page = OggPage(fileobj) + if not page.packets: + raise OggVorbisHeaderError("page has not packets") while not page.packets[0].startswith(b"\x01vorbis"): page = OggPage(fileobj) if not page.first: raise OggVorbisHeaderError( "page has ID header, but doesn't start a stream") + if len(page.packets[0]) < 28: + raise OggVorbisHeaderError( + "page contains a packet too short to be valid") (self.channels, self.sample_rate, max_bitrate, nominal_bitrate, - min_bitrate) = struct.unpack("= 15: + encoder_id = struct.unpack("> 4) + 4500) + self.encoder_info = "%s.%s" % (version[0], version[1:]) + else: + self.encoder_info = "" def pprint(self): return u"OptimFROG, %.2f seconds, %d Hz" % (self.length, diff --git a/libs/common/mutagen/py.typed b/libs/common/mutagen/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/common/mutagen/smf.py b/libs/common/mutagen/smf.py index f41d8142..5d664085 100644 --- a/libs/common/mutagen/smf.py +++ b/libs/common/mutagen/smf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2015 Christoph Reiter # # This program is free software; you can redistribute it and/or modify @@ -12,8 +11,7 @@ import struct from mutagen import StreamInfo, MutagenError from mutagen._file import FileType -from mutagen._util import loadfile -from mutagen._compat import xrange, endswith +from mutagen._util import loadfile, endswith class SMFError(MutagenError): @@ -123,7 +121,7 @@ def _read_midi_length(fileobj): # get a list of events and tempo changes for each track tracks = [] first_tempos = None - for tracknum in xrange(ntracks): + for tracknum in range(ntracks): identifier, chunk = read_chunk(fileobj) if identifier != b"MTrk": continue diff --git a/libs/common/mutagen/tak.py b/libs/common/mutagen/tak.py new file mode 100644 index 00000000..fefe7b71 --- /dev/null +++ b/libs/common/mutagen/tak.py @@ -0,0 +1,237 @@ +# Copyright (C) 2008 Lukáš Lalinský +# Copyright (C) 2019 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +"""Tom's lossless Audio Kompressor (TAK) streams with APEv2 tags. + +TAK is a lossless audio compressor developed by Thomas Becker. + +For more information, see: + +* http://www.thbeck.de/Tak/Tak.html +* http://wiki.hydrogenaudio.org/index.php?title=TAK +""" + +__all__ = ["TAK", "Open", "delete"] + +import struct + +from mutagen import StreamInfo +from mutagen.apev2 import ( + APEv2File, + delete, + error, +) +from mutagen._util import ( + BitReader, + BitReaderError, + convert_error, + enum, + endswith, +) + + +@enum +class TAKMetadata(object): + END = 0 + STREAM_INFO = 1 + SEEK_TABLE = 2 # Removed in TAK 1.1.1 + SIMPLE_WAVE_DATA = 3 + ENCODER_INFO = 4 + UNUSED_SPACE = 5 # New in TAK 1.0.3 + MD5 = 6 # New in TAK 1.1.1 + LAST_FRAME_INFO = 7 # New in TAK 1.1.1 + + +CRC_SIZE = 3 + +ENCODER_INFO_CODEC_BITS = 6 +ENCODER_INFO_PROFILE_BITS = 4 +ENCODER_INFO_TOTAL_BITS = ENCODER_INFO_CODEC_BITS + ENCODER_INFO_PROFILE_BITS + +SIZE_INFO_FRAME_DURATION_BITS = 4 +SIZE_INFO_SAMPLE_NUM_BITS = 35 +SIZE_INFO_TOTAL_BITS = (SIZE_INFO_FRAME_DURATION_BITS + + SIZE_INFO_SAMPLE_NUM_BITS) + +AUDIO_FORMAT_DATA_TYPE_BITS = 3 +AUDIO_FORMAT_SAMPLE_RATE_BITS = 18 +AUDIO_FORMAT_SAMPLE_BITS_BITS = 5 +AUDIO_FORMAT_CHANNEL_NUM_BITS = 4 +AUDIO_FORMAT_HAS_EXTENSION_BITS = 1 +AUDIO_FORMAT_BITS_MIN = 31 +AUDIO_FORMAT_BITS_MAX = 31 + 102 + +SAMPLE_RATE_MIN = 6000 +SAMPLE_BITS_MIN = 8 +CHANNEL_NUM_MIN = 1 + +STREAM_INFO_BITS_MIN = (ENCODER_INFO_TOTAL_BITS + + SIZE_INFO_TOTAL_BITS + + AUDIO_FORMAT_BITS_MIN) +STREAM_INFO_BITS_MAX = (ENCODER_INFO_TOTAL_BITS + + SIZE_INFO_TOTAL_BITS + + AUDIO_FORMAT_BITS_MAX) +STREAM_INFO_SIZE_MIN = (STREAM_INFO_BITS_MIN + 7) / 8 +STREAM_INFO_SIZE_MAX = (STREAM_INFO_BITS_MAX + 7) / 8 + + +class _LSBBitReader(BitReader): + """BitReader implementation which reads bits starting at LSB in each byte. + """ + + def _lsb(self, count): + value = self._buffer & 0xff >> (8 - count) + self._buffer = self._buffer >> count + self._bits -= count + return value + + def bits(self, count): + """Reads `count` bits and returns an uint, LSB read first. + + May raise BitReaderError if not enough data could be read or + IOError by the underlying file object. + """ + if count < 0: + raise ValueError + + value = 0 + if count <= self._bits: + value = self._lsb(count) + else: + # First read all available bits + shift = 0 + remaining = count + if self._bits > 0: + remaining -= self._bits + shift = self._bits + value = self._lsb(self._bits) + assert self._bits == 0 + + # Now add additional bytes + n_bytes = (remaining - self._bits + 7) // 8 + data = self._fileobj.read(n_bytes) + if len(data) != n_bytes: + raise BitReaderError("not enough data") + for b in bytearray(data): + if remaining > 8: # Use full byte + remaining -= 8 + value = (b << shift) | value + shift += 8 + else: + self._buffer = b + self._bits = 8 + b = self._lsb(remaining) + value = (b << shift) | value + + assert 0 <= self._bits < 8 + return value + + +class TAKHeaderError(error): + pass + + +class TAKInfo(StreamInfo): + + """TAK stream information. + + Attributes: + channels (`int`): number of audio channels + length (`float`): file length in seconds, as a float + sample_rate (`int`): audio sampling rate in Hz + bits_per_sample (`int`): audio sample size + encoder_info (`mutagen.text`): encoder version + """ + + channels = 0 + length = 0 + sample_rate = 0 + bitrate = 0 + encoder_info = "" + + @convert_error(IOError, TAKHeaderError) + @convert_error(BitReaderError, TAKHeaderError) + def __init__(self, fileobj): + stream_id = fileobj.read(4) + if len(stream_id) != 4 or not stream_id == b"tBaK": + raise TAKHeaderError("not a TAK file") + + bitreader = _LSBBitReader(fileobj) + while True: + type = TAKMetadata(bitreader.bits(7)) + bitreader.skip(1) # Unused + size = struct.unpack(" 0: + self.length = self.number_of_samples / float(self.sample_rate) + + def _parse_stream_info(self, bitreader, size): + if size < STREAM_INFO_SIZE_MIN or size > STREAM_INFO_SIZE_MAX: + raise TAKHeaderError("stream info has invalid length") + + # Encoder Info + bitreader.skip(ENCODER_INFO_CODEC_BITS) + bitreader.skip(ENCODER_INFO_PROFILE_BITS) + + # Size Info + bitreader.skip(SIZE_INFO_FRAME_DURATION_BITS) + self.number_of_samples = bitreader.bits(SIZE_INFO_SAMPLE_NUM_BITS) + + # Audio Format + bitreader.skip(AUDIO_FORMAT_DATA_TYPE_BITS) + self.sample_rate = (bitreader.bits(AUDIO_FORMAT_SAMPLE_RATE_BITS) + + SAMPLE_RATE_MIN) + self.bits_per_sample = (bitreader.bits(AUDIO_FORMAT_SAMPLE_BITS_BITS) + + SAMPLE_BITS_MIN) + self.channels = (bitreader.bits(AUDIO_FORMAT_CHANNEL_NUM_BITS) + + CHANNEL_NUM_MIN) + bitreader.skip(AUDIO_FORMAT_HAS_EXTENSION_BITS) + + def _parse_encoder_info(self, bitreader, size): + patch = bitreader.bits(8) + minor = bitreader.bits(8) + major = bitreader.bits(8) + self.encoder_info = "TAK %d.%d.%d" % (major, minor, patch) + + def pprint(self): + return u"%s, %d Hz, %d bits, %.2f seconds, %d channel(s)" % ( + self.encoder_info or "TAK", self.sample_rate, self.bits_per_sample, + self.length, self.channels) + + +class TAK(APEv2File): + """TAK(filething) + + Arguments: + filething (filething) + + Attributes: + info (`TAKInfo`) + """ + + _Info = TAKInfo + _mimes = ["audio/x-tak"] + + @staticmethod + def score(filename, fileobj, header): + return header.startswith(b"tBaK") + endswith(filename.lower(), ".tak") + + +Open = TAK diff --git a/libs/common/mutagen/trueaudio.py b/libs/common/mutagen/trueaudio.py index e62f4556..0d1e4a57 100644 --- a/libs/common/mutagen/trueaudio.py +++ b/libs/common/mutagen/trueaudio.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2006 Joe Wreschnig # # This program is free software; you can redistribute it and/or modify @@ -17,10 +16,9 @@ True Audio files use ID3 tags. __all__ = ["TrueAudio", "Open", "delete", "EasyTrueAudio"] -from ._compat import endswith from mutagen import StreamInfo from mutagen.id3 import ID3FileType, delete -from mutagen._util import cdata, MutagenError, convert_error +from mutagen._util import cdata, MutagenError, convert_error, endswith class error(MutagenError): @@ -99,4 +97,4 @@ class EasyTrueAudio(TrueAudio): """ from mutagen.easyid3 import EasyID3 as ID3 - ID3 = ID3 + ID3 = ID3 # type: ignore diff --git a/libs/common/mutagen/wave.py b/libs/common/mutagen/wave.py new file mode 100644 index 00000000..4e0c55f9 --- /dev/null +++ b/libs/common/mutagen/wave.py @@ -0,0 +1,209 @@ +# Copyright (C) 2017 Borewit +# Copyright (C) 2019-2020 Philipp Wolfer +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +"""Microsoft WAVE/RIFF audio file/stream information and tags.""" + +import sys +import struct + +from mutagen import StreamInfo, FileType + +from mutagen.id3 import ID3 +from mutagen._riff import RiffFile, InvalidChunk +from mutagen._iff import error as IffError +from mutagen.id3._util import ID3NoHeaderError, error as ID3Error +from mutagen._util import ( + convert_error, + endswith, + loadfile, + reraise, +) + +__all__ = ["WAVE", "Open", "delete"] + + +class error(IffError): + """WAVE stream parsing errors.""" + + +class _WaveFile(RiffFile): + """Representation of a RIFF/WAVE file""" + + def __init__(self, fileobj): + RiffFile.__init__(self, fileobj) + + if self.file_type != u'WAVE': + raise error("Expected RIFF/WAVE.") + + # Normalize ID3v2-tag-chunk to lowercase + if u'ID3' in self: + self[u'ID3'].id = u'id3' + + +class WaveStreamInfo(StreamInfo): + """WaveStreamInfo() + + Microsoft WAVE file information. + + Information is parsed from the 'fmt' & 'data'chunk of the RIFF/WAVE file + + Attributes: + length (`float`): audio length, in seconds + bitrate (`int`): audio bitrate, in bits per second + channels (`int`): The number of audio channels + sample_rate (`int`): audio sample rate, in Hz + bits_per_sample (`int`): The audio sample size + """ + + length = 0.0 + bitrate = 0 + channels = 0 + sample_rate = 0 + bits_per_sample = 0 + + SIZE = 16 + + @convert_error(IOError, error) + def __init__(self, fileobj): + """Raises error""" + + wave_file = _WaveFile(fileobj) + try: + format_chunk = wave_file[u'fmt'] + except KeyError as e: + raise error(str(e)) + + data = format_chunk.read() + if len(data) < 16: + raise InvalidChunk() + + # RIFF: http://soundfile.sapp.org/doc/WaveFormat/ + # Python struct.unpack: + # https://docs.python.org/2/library/struct.html#byte-order-size-and-alignment + info = struct.unpack(' 0: + try: + data_chunk = wave_file[u'data'] + self._number_of_samples = data_chunk.data_size / block_align + except KeyError: + pass + + if self.sample_rate > 0: + self.length = self._number_of_samples / self.sample_rate + + def pprint(self): + return u"%d channel RIFF @ %d bps, %s Hz, %.2f seconds" % ( + self.channels, self.bitrate, self.sample_rate, self.length) + + +class _WaveID3(ID3): + """A Wave file with ID3v2 tags""" + + def _pre_load_header(self, fileobj): + try: + fileobj.seek(_WaveFile(fileobj)[u'id3'].data_offset) + except (InvalidChunk, KeyError): + raise ID3NoHeaderError("No ID3 chunk") + + @convert_error(IOError, error) + @loadfile(writable=True) + def save(self, filething, v1=1, v2_version=4, v23_sep='/', padding=None): + """Save ID3v2 data to the Wave/RIFF file""" + + fileobj = filething.fileobj + wave_file = _WaveFile(fileobj) + + if u'id3' not in wave_file: + wave_file.insert_chunk(u'id3') + + chunk = wave_file[u'id3'] + + try: + data = self._prepare_data( + fileobj, chunk.data_offset, chunk.data_size, v2_version, + v23_sep, padding) + except ID3Error as e: + reraise(error, e, sys.exc_info()[2]) + + chunk.resize(len(data)) + chunk.write(data) + + def delete(self, filething): + """Completely removes the ID3 chunk from the RIFF/WAVE file""" + + delete(filething) + self.clear() + + +@convert_error(IOError, error) +@loadfile(method=False, writable=True) +def delete(filething): + """Completely removes the ID3 chunk from the RIFF/WAVE file""" + + try: + _WaveFile(filething.fileobj).delete_chunk(u'id3') + except KeyError: + pass + + +class WAVE(FileType): + """WAVE(filething) + + A Waveform Audio File Format + (WAVE, or more commonly known as WAV due to its filename extension) + + Arguments: + filething (filething) + + Attributes: + tags (`mutagen.id3.ID3`) + info (`WaveStreamInfo`) + """ + + _mimes = ["audio/wav", "audio/wave"] + + @staticmethod + def score(filename, fileobj, header): + filename = filename.lower() + + return (header.startswith(b"RIFF") + (header[8:12] == b'WAVE') + + endswith(filename, b".wav") + endswith(filename, b".wave")) + + def add_tags(self): + """Add an empty ID3 tag to the file.""" + if self.tags is None: + self.tags = _WaveID3() + else: + raise error("an ID3 tag already exists") + + @convert_error(IOError, error) + @loadfile() + def load(self, filething, **kwargs): + """Load stream and tag information from a file.""" + + fileobj = filething.fileobj + self.info = WaveStreamInfo(fileobj) + fileobj.seek(0, 0) + + try: + self.tags = _WaveID3(fileobj, **kwargs) + except ID3NoHeaderError: + self.tags = None + except ID3Error as e: + raise error(e) + else: + self.tags.filename = self.filename + + +Open = WAVE diff --git a/libs/common/mutagen/wavpack.py b/libs/common/mutagen/wavpack.py index 290b90c3..c2515eef 100644 --- a/libs/common/mutagen/wavpack.py +++ b/libs/common/mutagen/wavpack.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2006 Joe Wreschnig # 2014 Christoph Reiter # @@ -76,9 +75,10 @@ class WavPackInfo(StreamInfo): Attributes: channels (int): number of audio channels (1 or 2) - length (float: file length in seconds, as a float + length (float): file length in seconds, as a float sample_rate (int): audio sampling rate in Hz - version (int) WavPack stream version + bits_per_sample (int): audio sample size + version (int): WavPack stream version """ def __init__(self, fileobj): @@ -90,6 +90,12 @@ class WavPackInfo(StreamInfo): self.version = header.version self.channels = bool(header.flags & 4) or 2 self.sample_rate = RATES[(header.flags >> 23) & 0xF] + self.bits_per_sample = ((header.flags & 3) + 1) * 8 + + # most common multiplier (DSD64) + if (header.flags >> 31) & 1: + self.sample_rate *= 4 + self.bits_per_sample = 1 if header.total_samples == -1 or header.block_index != 0: # TODO: we could make this faster by using the tag size @@ -114,6 +120,15 @@ class WavPackInfo(StreamInfo): class WavPack(APEv2File): + """WavPack(filething) + + Arguments: + filething (filething) + + Attributes: + info (`WavPackInfo`) + """ + _Info = WavPackInfo _mimes = ["audio/x-wavpack"] diff --git a/libs/common/share/man/man1/mid3cp.1 b/libs/common/share/man/man1/mid3cp.1 new file mode 100644 index 00000000..02c97f96 --- /dev/null +++ b/libs/common/share/man/man1/mid3cp.1 @@ -0,0 +1,66 @@ +.\" Man page generated from reStructuredText. +. +.TH MID3CP 1 "" "" "" +.SH NAME +mid3cp \- copy ID3 tags +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmid3cp\fP [\fIoptions\fP] \fIsource\fP \fIdest\fP +.SH DESCRIPTION +.sp +\fBmid3cp\fP copies the ID3 tags from a source file to a destination file. +.sp +It is designed to provide similar functionality to id3lib\(aqs id3cp tool, and can +optionally write ID3v1 tags. It can also exclude specific tags from being +copied. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-\-verbose\fP,\fB \-v +Be verbose: state all operations performed, and list tags in source file. +.TP +.B \-\-write\-v1 +Write ID3v1 tags to the destination file, derived from the ID3v2 tags. +.TP +.B \-\-exclude\-tag\fP,\fB \-x +Exclude a specific tag from being copied. Can be specified multiple times. +.TP +.B \-\-merge +Copy over frames instead of replacing the whole ID3 tag. The tag version +of \fIdest\fP will be used. In case \fIdest\fP has no ID3 tag this option has no +effect. +.UNINDENT +.SH AUTHOR +.sp +Marcus Sundman. +.sp +Based on id3cp (part of id3lib) by Dirk Mahoney and Scott Thomas Haug. +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/share/man/man1/mid3iconv.1 b/libs/common/share/man/man1/mid3iconv.1 new file mode 100644 index 00000000..c6916b09 --- /dev/null +++ b/libs/common/share/man/man1/mid3iconv.1 @@ -0,0 +1,68 @@ +.\" Man page generated from reStructuredText. +. +.TH MID3ICONV 1 "" "" "" +.SH NAME +mid3iconv \- convert ID3 tag encodings +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmid3iconv\fP [\fIoptions\fP] \fIfilename\fP ... +.SH DESCRIPTION +.sp +\fBmid3iconv\fP converts ID3 tags from legacy encodings to Unicode and stores +them using the ID3v2 format. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-\-debug\fP,\fB \-d +Print updated tags +.TP +.B \-\-dry\-run\fP,\fB \-p +Do not actually modify files +.TP +.B \-\-encoding\fP,\fB \-e +Convert from this encoding. By default, your locale\(aqs default encoding is +used. +.TP +.B \-\-force\-v1 +Use an ID3v1 tag even if an ID3v2 tag is present +.TP +.B \-\-quiet\fP,\fB \-q +Only output errors +.TP +.B \-\-remove\-v1 +Remove any ID3v1 tag after processing the files +.UNINDENT +.SH AUTHOR +.sp +Emfox Zhou. +.sp +Based on id3iconv (\fI\%http://www.cs.berkeley.edu/~zf/id3iconv/\fP) by Feng Zhou. +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/share/man/man1/mid3v2.1 b/libs/common/share/man/man1/mid3v2.1 new file mode 100644 index 00000000..fd5f866c --- /dev/null +++ b/libs/common/share/man/man1/mid3v2.1 @@ -0,0 +1,151 @@ +.\" Man page generated from reStructuredText. +. +.TH MID3V2 1 "" "" "" +.SH NAME +mid3v2 \- audio tag editor similar to 'id3v2' +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmid3v2\fP [\fIoptions\fP] \fIfilename\fP ... +.SH DESCRIPTION +.sp +\fBmid3v2\fP is a Mutagen\-based replacement for id3lib\(aqs id3v2. It supports +ID3v2.4 and more frames; it also does not have the numerous bugs that plague +id3v2. +.sp +This program exists mostly for compatibility with programs that want to tag +files using id3v2. For a more usable interface, we recommend Ex Falso. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-q\fP,\fB \-\-quiet +Be quiet: do not mention file operations that perform the user\(aqs +request. Warnings will still be printed. +.TP +.B \-v\fP,\fB \-\-verbose +Be verbose: state all operations performed. This is the opposite of +\-\-quiet. This is the default. +.TP +.B \-e\fP,\fB \-\-escape +Enable interpretation of backslash escapes for tag values. +Makes it possible to escape the colon\-separator in TXXX, WXXX, COMM +values like \(aq\e:\(aq and insert escape sequences like \(aq\en\(aq, \(aq\et\(aq etc. +.TP +.B \-f\fP,\fB \-\-list\-frames +Display all supported ID3v2.3/2.4 frames and their meanings. +.TP +.B \-L\fP,\fB \-\-list\-genres +List all ID3v1 numeric genres. These can be used to set TCON frames, +but it is not recommended. +.TP +.B \-l\fP,\fB \-\-list +List all tags in the files. The output format is \fInot\fP the same as +id3v2\(aqs; instead, it is easily parsable and readable. Some tags may not +have human\-readable representations. +.TP +.B \-\-list\-raw +List all tags in the files, in raw format. Although this format is +nominally human\-readable, it may be very long if the tag contains +embedded binary data. +.TP +.B \-d\fP,\fB \-\-delete\-v2 +Delete ID3v2 tags. +.TP +.B \-s\fP,\fB \-\-delete\-v1 +Delete ID3v1 tags. +.TP +.B \-D\fP,\fB \-\-delete\-all +Delete all ID3 tags. +.TP +.BI \-\-delete\-frames\fB= FRAMES +Delete specific ID3v2 frames (or groups of frames) from the files. +\fBFRAMES\fP is a "," separated list of frame names e.g. \fB"TPE1,TALB"\fP +.TP +.B \-C\fP,\fB \-\-convert +Convert ID3v1 tags to ID3v2 tags. This will also happen automatically +during any editing. +.TP +.BI \-a\fP,\fB \-\-artist\fB= ARTIST +Set the artist information (TPE1). +.TP +.BI \-A\fP,\fB \-\-album\fB= ALBUM +Set the album information (TALB). +.TP +.BI \-t\fP,\fB \-\-song\fB= TITLE +Set the title information (TIT2). +.TP +.BI \-c\fP,\fB \-\-comment\fB= +Set a comment (COMM). The language and description may be omitted, in +which case the language defaults to English, and the description to an +empty string. +.TP +.BI \-p\fP,\fB \-\-picture\fB= +Set the attached picture (APIC). Everything except the filename can be +omitted in which case default values will be used. +.TP +.BI \-g\fP,\fB \-\-genre\fB= GENRE +Set the genre information (TCON). +.TP +.BI \-y\fP,\fB \-\-year\fB= \fP,\fB \ \-\-date\fB= +Set the year/date information (TDRC). +.TP +.BI \-T\fP,\fB \-\-track\fB= +Set the track number (TRCK). +.UNINDENT +.sp +Any text or URL frame (those beginning with T or W) can be modified or +added by prefixing the name of the frame with "\-\-". For example, \fB\-\-TIT3 +"Monkey!"\fP will set the TIT3 (subtitle) frame to \fBMonkey!\fP\&. +.sp +The TXXX frame has the format ; many TXXX frames may be +set in the file as long as they have different keys. To set this key, just +separate the text with a colon, e.g. \fB\-\-TXXX "ALBUMARTISTSORT:Examples, +The"\fP\&. The description can be omitted in which case it defaults to an empty +string. +.sp +The WXXX frame has the same format as TXXX but since URLs usually contain a +":" you have provide a description or enable escaping (\-e): +\fB\-\-WXXX "desc:http://foo.bar"\fP or \fB\-e \-\-WXXX "http\e\e://foo.bar"\fP +.sp +The USLT frame has the format . The language and +description may be omitted, in which case the language defaults to English, +and the description to an empty string. +.sp +The special POPM frame can be set in a similar way: \fB\-\-POPM +"bob@example.com:128:2"\fP to set Bob\(aqs rating to 128/255 with 2 plays. +.SH BUGS +.sp +No sanity checking is done on the editing operations you perform, so mid3v2 +will happily accept \-\-TSIZ when editing an ID3v2.4 frame. However, it will +also automatically throw it out during the next edit operation. +.SH AUTHOR +.sp +Joe Wreschnig is the author of mid3v2, but he doesn\(aqt like to admit it. +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/share/man/man1/moggsplit.1 b/libs/common/share/man/man1/moggsplit.1 new file mode 100644 index 00000000..5ca456cf --- /dev/null +++ b/libs/common/share/man/man1/moggsplit.1 @@ -0,0 +1,64 @@ +.\" Man page generated from reStructuredText. +. +.TH MOGGSPLIT 1 "" "" "" +.SH NAME +moggsplit \- split Ogg logical streams +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmoggsplit\fP \fIfilename\fP ... +.SH DESCRIPTION +.sp +\fBmoggsplit\fP splits a multiplexed Ogg stream into separate files. For +example, it can separate an OGM into separate Ogg DivX and Ogg Vorbis +streams, or a chained Ogg Vorbis file into two separate files. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-\-extension +Use the supplied extension when generating new files; the default is +\fBogg\fP\&. +.TP +.B \-\-pattern +Use the supplied pattern when generating new files. This is a Python +keyword format string with three variables, \fIbase\fP for the original +file\(aqs base name, \fIstream\fP for the stream\(aqs serial number, and ext for +the extension give by \fB\-\-extension\fP\&. +.sp +The default is \fB%(base)s\-%(stream)d.%(ext)s\fP\&. +.TP +.B \-\-m3u +Generate an m3u playlist along with the newly generated files. Useful +for large chained Oggs. +.UNINDENT +.SH AUTHOR +.sp +Joe Wreschnig +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/share/man/man1/mutagen-inspect.1 b/libs/common/share/man/man1/mutagen-inspect.1 new file mode 100644 index 00000000..6d4dc42b --- /dev/null +++ b/libs/common/share/man/man1/mutagen-inspect.1 @@ -0,0 +1,47 @@ +.\" Man page generated from reStructuredText. +. +.TH MUTAGEN-INSPECT 1 "" "" "" +.SH NAME +mutagen-inspect \- view Mutagen-supported audio tags +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmutagen\-inspect\fP \fIfilename\fP ... +.SH DESCRIPTION +.sp +\fBmutagen\-inspect\fP loads and prints information about an audio file and +its tags. +.sp +It is primarily intended as a debugging tool for Mutagen, but can be useful +for extracting tags from the command line. +.SH AUTHOR +.sp +Joe Wreschnig +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/share/man/man1/mutagen-pony.1 b/libs/common/share/man/man1/mutagen-pony.1 new file mode 100644 index 00000000..b603fc3a --- /dev/null +++ b/libs/common/share/man/man1/mutagen-pony.1 @@ -0,0 +1,46 @@ +.\" Man page generated from reStructuredText. +. +.TH MUTAGEN-PONY 1 "" "" "" +.SH NAME +mutagen-pony \- scan a collection of MP3 files +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SH SYNOPSIS +.sp +\fBmutagen\-pony\fP \fIdirectory\fP ... +.SH DESCRIPTION +.sp +\fBmutagen\-pony\fP scans any directories given and reports on the kinds of +tags in the MP3s it finds in them. Ride the pony. +.sp +It is primarily intended as a debugging tool for Mutagen. +.SH AUTHORS +.sp +Michael Urman and Joe Wreschnig +.\" Generated by docutils manpage writer. +. diff --git a/libs/common/unidecode/__init__.py b/libs/common/unidecode/__init__.py index 5d968fdb..5633c458 100644 --- a/libs/common/unidecode/__init__.py +++ b/libs/common/unidecode/__init__.py @@ -3,35 +3,39 @@ """Transliterate Unicode text into plain 7-bit ASCII. Example usage: + >>> from unidecode import unidecode ->>> unidecode(u"\u5317\u4EB0") +>>> unidecode("\u5317\u4EB0") "Bei Jing " The transliteration uses a straightforward map, and doesn't have alternatives for the same character based on language, position, or anything else. -In Python 3, a standard string object will be returned. If you need bytes, use: +A standard string object will be returned. If you need bytes, use: + >>> unidecode("Κνωσός").encode("ascii") b'Knosos' """ import warnings -from sys import version_info +from typing import Dict, Optional, Sequence -Cache = {} +Cache = {} # type: Dict[int, Optional[Sequence[Optional[str]]]] + +class UnidecodeError(ValueError): + def __init__(self, message: str, index: Optional[int] = None) -> None: + """Raised for Unidecode-related errors. + + The index attribute contains the index of the character that caused + the error. + """ + super(UnidecodeError, self).__init__(message) + self.index = index -def _warn_if_not_unicode(string): - if version_info[0] < 3 and not isinstance(string, unicode): - warnings.warn( "Argument %r is not an unicode object. " - "Passing an encoded string will likely have " - "unexpected results." % (type(string),), - RuntimeWarning, 2) - - -def unidecode_expect_ascii(string): +def unidecode_expect_ascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str: """Transliterate an Unicode object into an ASCII string - >>> unidecode(u"\u5317\u4EB0") + >>> unidecode("\u5317\u4EB0") "Bei Jing " This function first tries to convert the string using ASCII codec. @@ -39,65 +43,96 @@ def unidecode_expect_ascii(string): transliteration using the character tables. This is approx. five times faster if the string only contains ASCII - characters, but slightly slower than using unidecode directly if non-ASCII - chars are present. + characters, but slightly slower than unicode_expect_nonascii if + non-ASCII characters are present. + + errors specifies what to do with characters that have not been + found in replacement tables. The default is 'ignore' which ignores + the character. 'strict' raises an UnidecodeError. 'replace' + substitutes the character with replace_str (default is '?'). + 'preserve' keeps the original character. + + Note that if 'preserve' is used the returned string might not be + ASCII! """ - _warn_if_not_unicode(string) try: bytestring = string.encode('ASCII') except UnicodeEncodeError: - return _unidecode(string) - if version_info[0] >= 3: - return string + pass else: - return bytestring + return string -def unidecode_expect_nonascii(string): + return _unidecode(string, errors, replace_str) + +def unidecode_expect_nonascii(string: str, errors: str = 'ignore', replace_str: str = '?') -> str: """Transliterate an Unicode object into an ASCII string - >>> unidecode(u"\u5317\u4EB0") + >>> unidecode("\u5317\u4EB0") "Bei Jing " + + See unidecode_expect_ascii. """ - _warn_if_not_unicode(string) - return _unidecode(string) + return _unidecode(string, errors, replace_str) unidecode = unidecode_expect_ascii -def _unidecode(string): +def _get_repl_str(char: str) -> Optional[str]: + codepoint = ord(char) + + if codepoint < 0x80: + # Already ASCII + return str(char) + + if codepoint > 0xeffff: + # No data on characters in Private Use Area and above. + return None + + if 0xd800 <= codepoint <= 0xdfff: + warnings.warn( "Surrogate character %r will be ignored. " + "You might be using a narrow Python build." % (char,), + RuntimeWarning, 2) + + section = codepoint >> 8 # Chop off the last two hex digits + position = codepoint % 256 # Last two hex digits + + try: + table = Cache[section] + except KeyError: + try: + mod = __import__('unidecode.x%03x'%(section), globals(), locals(), ['data']) + except ImportError: + # No data on this character + Cache[section] = None + return None + + Cache[section] = table = mod.data + + if table and len(table) > position: + return table[position] + else: + return None + +def _unidecode(string: str, errors: str, replace_str:str) -> str: retval = [] - for char in string: - codepoint = ord(char) + for index, char in enumerate(string): + repl = _get_repl_str(char) - if codepoint < 0x80: # Basic ASCII - retval.append(str(char)) - continue - - if codepoint > 0xeffff: - continue # Characters in Private Use Area and above are ignored + if repl is None: + if errors == 'ignore': + repl = '' + elif errors == 'strict': + raise UnidecodeError('no replacement found for character %r ' + 'in position %d' % (char, index), index) + elif errors == 'replace': + repl = replace_str + elif errors == 'preserve': + repl = char + else: + raise UnidecodeError('invalid value for errors parameter %r' % (errors,)) - if 0xd800 <= codepoint <= 0xdfff: - warnings.warn( "Surrogate character %r will be ignored. " - "You might be using a narrow Python build." % (char,), - RuntimeWarning, 2) - - section = codepoint >> 8 # Chop off the last two hex digits - position = codepoint % 256 # Last two hex digits - - try: - table = Cache[section] - except KeyError: - try: - mod = __import__('unidecode.x%03x'%(section), globals(), locals(), ['data']) - except ImportError: - Cache[section] = None - continue # No match: ignore this character and carry on. - - Cache[section] = table = mod.data - - if table and len(table) > position: - retval.append( table[position] ) + retval.append(repl) return ''.join(retval) diff --git a/libs/common/unidecode/__init__.pyi b/libs/common/unidecode/__init__.pyi new file mode 100644 index 00000000..5963674c --- /dev/null +++ b/libs/common/unidecode/__init__.pyi @@ -0,0 +1,11 @@ +from typing import Any, Dict, Optional, Sequence + +Cache: Dict[int, Optional[Sequence[Optional[str]]]] + +class UnidecodeError(ValueError): + index: Optional[int] = ... + def __init__(self, message: str, index: Optional[int] = ...) -> None: ... + +def unidecode_expect_ascii(string: str, errors: str = ..., replace_str: str = ...) -> str: ... +def unidecode_expect_nonascii(string: str, errors: str = ..., replace_str: str = ...) -> str: ... +unidecode = unidecode_expect_ascii diff --git a/libs/common/unidecode/__main__.py b/libs/common/unidecode/__main__.py new file mode 100644 index 00000000..390f842a --- /dev/null +++ b/libs/common/unidecode/__main__.py @@ -0,0 +1,3 @@ +from unidecode.util import main + +main() diff --git a/libs/common/unidecode/py.typed b/libs/common/unidecode/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/libs/common/unidecode/util.py b/libs/common/unidecode/util.py index 477280d1..05f08bd8 100644 --- a/libs/common/unidecode/util.py +++ b/libs/common/unidecode/util.py @@ -1,15 +1,12 @@ # vim:ts=4 sw=4 expandtab softtabstop=4 -from __future__ import print_function -import optparse +import argparse +import io import locale import os import sys -import warnings from unidecode import unidecode -PY3 = sys.version_info[0] >= 3 - def fatal(msg): sys.stderr.write(msg + "\n") sys.exit(1) @@ -17,42 +14,38 @@ def fatal(msg): def main(): default_encoding = locale.getpreferredencoding() - parser = optparse.OptionParser('%prog [options] [FILE]', + parser = argparse.ArgumentParser( description="Transliterate Unicode text into ASCII. FILE is path to file to transliterate. " "Standard input is used if FILE is omitted and -c is not specified.") - parser.add_option('-e', '--encoding', metavar='ENCODING', default=default_encoding, + parser.add_argument('-e', '--encoding', metavar='ENCODING', default=default_encoding, help='Specify an encoding (default is %s)' % (default_encoding,)) - parser.add_option('-c', metavar='TEXT', dest='text', + parser.add_argument('-c', metavar='TEXT', dest='text', help='Transliterate TEXT instead of FILE') + parser.add_argument('path', nargs='?', metavar='FILE') - options, args = parser.parse_args() + args = parser.parse_args() - encoding = options.encoding + encoding = args.encoding - if args: - if options.text: + if args.path: + if args.text: fatal("Can't use both FILE and -c option") else: - with open(args[0], 'rb') as f: - stream = f.read() - elif options.text: - if PY3: - stream = os.fsencode(options.text) - else: - stream = options.text + stream = open(args.path, 'rb') + elif args.text: + text = os.fsencode(args.text) # add a newline to the string if it comes from the # command line so that the result is printed nicely # on the console. - stream += '\n'.encode('ascii') + stream = io.BytesIO(text + b'\n') else: - if PY3: - stream = sys.stdin.buffer.read() - else: - stream = sys.stdin.read() + stream = sys.stdin.buffer - try: - stream = stream.decode(encoding) - except UnicodeDecodeError as e: - fatal('Unable to decode input: %s, start: %d, end: %d' % (e.reason, e.start, e.end)) + for line_nr, line in enumerate(stream): + try: + line = line.decode(encoding) + except UnicodeDecodeError as e: + fatal('Unable to decode input line %s: %s, start: %d, end: %d' % (line_nr, e.reason, e.start, e.end)) - sys.stdout.write(unidecode(stream)) + sys.stdout.write(unidecode(line)) + stream.close() diff --git a/libs/common/unidecode/x000.py b/libs/common/unidecode/x000.py index 27e8d684..2c288a68 100644 --- a/libs/common/unidecode/x000.py +++ b/libs/common/unidecode/x000.py @@ -76,9 +76,9 @@ data = ( '1', # 0xb9 'o', # 0xba '>>', # 0xbb -' 1/4 ', # 0xbc -' 1/2 ', # 0xbd -' 3/4 ', # 0xbe +' 1/4', # 0xbc +' 1/2', # 0xbd +' 3/4', # 0xbe '?', # 0xbf 'A', # 0xc0 'A', # 0xc1 diff --git a/libs/common/unidecode/x002.py b/libs/common/unidecode/x002.py index d7028cdf..710942c8 100644 --- a/libs/common/unidecode/x002.py +++ b/libs/common/unidecode/x002.py @@ -64,8 +64,8 @@ data = ( 'T', # 0x3e 's', # 0x3f 'z', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 +None, # 0x41 +None, # 0x42 'B', # 0x43 'U', # 0x44 '^', # 0x45 @@ -238,20 +238,20 @@ data = ( 'V', # 0xec '=', # 0xed '"', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x003.py b/libs/common/unidecode/x003.py index 4ba8d726..215faf2b 100644 --- a/libs/common/unidecode/x003.py +++ b/libs/common/unidecode/x003.py @@ -78,23 +78,23 @@ data = ( '', # 0x4c '', # 0x4d '', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f '', # 0x60 '', # 0x61 '', # 0x62 @@ -111,26 +111,26 @@ data = ( 't', # 0x6d 'v', # 0x6e 'x', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 '\'', # 0x74 ',', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 '', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d +None, # 0x7b +None, # 0x7c +None, # 0x7d '?', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 '', # 0x84 '', # 0x85 'A', # 0x86 @@ -138,9 +138,9 @@ data = ( 'E', # 0x88 'E', # 0x89 'I', # 0x8a -'[?]', # 0x8b +None, # 0x8b 'O', # 0x8c -'[?]', # 0x8d +None, # 0x8d 'U', # 0x8e 'O', # 0x8f 'I', # 0x90 @@ -161,7 +161,7 @@ data = ( 'O', # 0x9f 'P', # 0xa0 'R', # 0xa1 -'[?]', # 0xa2 +None, # 0xa2 'S', # 0xa3 'T', # 0xa4 'U', # 0xa5 @@ -206,7 +206,7 @@ data = ( 'o', # 0xcc 'u', # 0xcd 'o', # 0xce -'[?]', # 0xcf +None, # 0xcf 'b', # 0xd0 'th', # 0xd1 'U', # 0xd2 @@ -215,8 +215,8 @@ data = ( 'ph', # 0xd5 'p', # 0xd6 '&', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 +None, # 0xd8 +None, # 0xd9 'St', # 0xda 'st', # 0xdb 'W', # 0xdc @@ -243,15 +243,15 @@ data = ( 'r', # 0xf1 'c', # 0xf2 'j', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x004.py b/libs/common/unidecode/x004.py index 1cc3dbc4..d2b5ff5f 100644 --- a/libs/common/unidecode/x004.py +++ b/libs/common/unidecode/x004.py @@ -134,11 +134,11 @@ data = ( '', # 0x84 '', # 0x85 '', # 0x86 -'[?]', # 0x87 +None, # 0x87 '*100.000*', # 0x88 '*1.000.000*', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b +None, # 0x8a +None, # 0x8b '"', # 0x8c '"', # 0x8d 'R\'', # 0x8e @@ -196,17 +196,17 @@ data = ( 'zh', # 0xc2 'K\'', # 0xc3 'k\'', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 +None, # 0xc5 +None, # 0xc6 'N\'', # 0xc7 'n\'', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca +None, # 0xc9 +None, # 0xca 'Ch', # 0xcb 'ch', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xcd +None, # 0xce +None, # 0xcf 'a', # 0xd0 'a', # 0xd1 'A', # 0xd2 @@ -245,13 +245,13 @@ data = ( 'u', # 0xf3 'Ch', # 0xf4 'ch', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 +None, # 0xf6 +None, # 0xf7 'Y', # 0xf8 'y', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x005.py b/libs/common/unidecode/x005.py index ced54426..8779da6a 100644 --- a/libs/common/unidecode/x005.py +++ b/libs/common/unidecode/x005.py @@ -1,53 +1,53 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f -'[?]', # 0x20 -'[?]', # 0x21 -'[?]', # 0x22 -'[?]', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 'A', # 0x31 'B', # 0x32 'G', # 0x33 @@ -86,8 +86,8 @@ data = ( 'K`', # 0x54 'O', # 0x55 'F', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 +None, # 0x57 +None, # 0x58 '<', # 0x59 '\'', # 0x5a '/', # 0x5b @@ -95,7 +95,7 @@ data = ( ',', # 0x5d '?', # 0x5e '.', # 0x5f -'[?]', # 0x60 +None, # 0x60 'a', # 0x61 'b', # 0x62 'g', # 0x63 @@ -135,15 +135,15 @@ data = ( 'o', # 0x85 'f', # 0x86 'ew', # 0x87 -'[?]', # 0x88 +None, # 0x88 ':', # 0x89 '-', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 '', # 0x91 '', # 0x92 '', # 0x93 @@ -175,7 +175,7 @@ data = ( '', # 0xad '', # 0xae '', # 0xaf -'@', # 0xb0 +'', # 0xb0 'e', # 0xb1 'a', # 0xb2 'o', # 0xb3 @@ -187,26 +187,26 @@ data = ( 'o', # 0xb9 'o', # 0xba 'u', # 0xbb -'\'', # 0xbc +'', # 0xbc '', # 0xbd '-', # 0xbe -'-', # 0xbf +'', # 0xbf '|', # 0xc0 '', # 0xc1 '', # 0xc2 -':', # 0xc3 +'.', # 0xc3 '', # 0xc4 '', # 0xc5 'n', # 0xc6 'o', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf 'A', # 0xd0 'b', # 0xd1 'g', # 0xd2 @@ -214,11 +214,11 @@ data = ( 'h', # 0xd4 'v', # 0xd5 'z', # 0xd6 -'KH', # 0xd7 -'t', # 0xd8 +'H', # 0xd7 +'T', # 0xd8 'y', # 0xd9 -'k', # 0xda -'k', # 0xdb +'KH', # 0xda +'KH', # 0xdb 'l', # 0xdc 'm', # 0xdd 'm', # 0xde @@ -230,28 +230,28 @@ data = ( 'p', # 0xe4 'TS', # 0xe5 'TS', # 0xe6 -'q', # 0xe7 +'k', # 0xe7 'r', # 0xe8 'SH', # 0xe9 't', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +'YYY', # 0xef 'V', # 0xf0 'OY', # 0xf1 -'i', # 0xf2 +'EY', # 0xf2 '\'', # 0xf3 '"', # 0xf4 -'v', # 0xf5 -'n', # 0xf6 -'q', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x006.py b/libs/common/unidecode/x006.py index 09440b28..79296330 100644 --- a/libs/common/unidecode/x006.py +++ b/libs/common/unidecode/x006.py @@ -1,37 +1,37 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b ',', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a ';', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e +None, # 0x1c +None, # 0x1d +None, # 0x1e '?', # 0x1f -'[?]', # 0x20 +None, # 0x20 '', # 0x21 'a', # 0x22 '\'', # 0x23 @@ -58,11 +58,11 @@ data = ( 'Z', # 0x38 '`', # 0x39 'G', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f '', # 0x40 'f', # 0x41 'q', # 0x42 @@ -85,16 +85,16 @@ data = ( '', # 0x53 '\'', # 0x54 '\'', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f '0', # 0x60 '1', # 0x61 '2', # 0x62 @@ -109,8 +109,8 @@ data = ( '.', # 0x6b ',', # 0x6c '*', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f +None, # 0x6e +None, # 0x6f '', # 0x70 '\'', # 0x71 '\'', # 0x72 @@ -237,8 +237,8 @@ data = ( '', # 0xeb '', # 0xec '', # 0xed -'[?]', # 0xee -'[?]', # 0xef +None, # 0xee +None, # 0xef '0', # 0xf0 '1', # 0xf1 '2', # 0xf2 diff --git a/libs/common/unidecode/x007.py b/libs/common/unidecode/x007.py index d2c00213..f2b3ecd7 100644 --- a/libs/common/unidecode/x007.py +++ b/libs/common/unidecode/x007.py @@ -13,7 +13,7 @@ data = ( '{', # 0x0b '}', # 0x0c '*', # 0x0d -'[?]', # 0x0e +None, # 0x0e '', # 0x0f '\'', # 0x10 '', # 0x11 @@ -44,9 +44,9 @@ data = ( 'r', # 0x2a 'sh', # 0x2b 't', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f +None, # 0x2d +None, # 0x2e +None, # 0x2f 'a', # 0x30 'a', # 0x31 'a', # 0x32 @@ -74,59 +74,59 @@ data = ( '@', # 0x48 '|', # 0x49 '+', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f 'h', # 0x80 'sh', # 0x81 'n', # 0x82 @@ -176,82 +176,82 @@ data = ( 'o', # 0xae 'oa', # 0xaf '', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x009.py b/libs/common/unidecode/x009.py index 564ec784..c910be2f 100644 --- a/libs/common/unidecode/x009.py +++ b/libs/common/unidecode/x009.py @@ -1,9 +1,9 @@ data = ( -'[?]', # 0x00 +None, # 0x00 'N', # 0x01 'N', # 0x02 'H', # 0x03 -'[?]', # 0x04 +None, # 0x04 'a', # 0x05 'aa', # 0x06 'i', # 0x07 @@ -57,8 +57,8 @@ data = ( 'ss', # 0x37 's', # 0x38 'h', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b +None, # 0x3a +None, # 0x3b '\'', # 0x3c '\'', # 0x3d 'aa', # 0x3e @@ -77,16 +77,16 @@ data = ( 'o', # 0x4b 'au', # 0x4c '', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f +None, # 0x4e +None, # 0x4f 'AUM', # 0x50 '\'', # 0x51 '\'', # 0x52 '`', # 0x53 '\'', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 +None, # 0x55 +None, # 0x56 +None, # 0x57 'q', # 0x58 'khh', # 0x59 'ghh', # 0x5a @@ -112,26 +112,26 @@ data = ( '8', # 0x6e '9', # 0x6f '.', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 'N', # 0x81 'N', # 0x82 'H', # 0x83 -'[?]', # 0x84 +None, # 0x84 'a', # 0x85 'aa', # 0x86 'i', # 0x87 @@ -140,12 +140,12 @@ data = ( 'uu', # 0x8a 'R', # 0x8b 'RR', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e +None, # 0x8d +None, # 0x8e 'e', # 0x8f 'ai', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 +None, # 0x91 +None, # 0x92 'o', # 0x93 'au', # 0x94 'k', # 0x95 @@ -168,7 +168,7 @@ data = ( 'd', # 0xa6 'dh', # 0xa7 'n', # 0xa8 -'[?]', # 0xa9 +None, # 0xa9 'p', # 0xaa 'ph', # 0xab 'b', # 0xac @@ -176,19 +176,19 @@ data = ( 'm', # 0xae 'y', # 0xaf 'r', # 0xb0 -'[?]', # 0xb1 +None, # 0xb1 'l', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 'sh', # 0xb6 'ss', # 0xb7 's', # 0xb8 'h', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb +None, # 0xba +None, # 0xbb '\'', # 0xbc -'[?]', # 0xbd +None, # 0xbd 'aa', # 0xbe 'i', # 0xbf 'ii', # 0xc0 @@ -196,39 +196,39 @@ data = ( 'uu', # 0xc2 'R', # 0xc3 'RR', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 +None, # 0xc5 +None, # 0xc6 'e', # 0xc7 'ai', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca +None, # 0xc9 +None, # 0xca 'o', # 0xcb 'au', # 0xcc '', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 '+', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb 'rr', # 0xdc 'rh', # 0xdd -'[?]', # 0xde +None, # 0xde 'yy', # 0xdf 'RR', # 0xe0 'LL', # 0xe1 'L', # 0xe2 'LL', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 +None, # 0xe4 +None, # 0xe5 '0', # 0xe6 '1', # 0xe7 '2', # 0xe8 @@ -250,8 +250,8 @@ data = ( ' 1 - 1/', # 0xf8 '/16', # 0xf9 '', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00a.py b/libs/common/unidecode/x00a.py index 1ccd9df6..26c43a58 100644 --- a/libs/common/unidecode/x00a.py +++ b/libs/common/unidecode/x00a.py @@ -1,23 +1,23 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 +None, # 0x00 +None, # 0x01 'N', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 +None, # 0x03 +None, # 0x04 'a', # 0x05 'aa', # 0x06 'i', # 0x07 'ii', # 0x08 'u', # 0x09 'uu', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e 'ee', # 0x0f 'ai', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 +None, # 0x11 +None, # 0x12 'oo', # 0x13 'au', # 0x14 'k', # 0x15 @@ -40,7 +40,7 @@ data = ( 'd', # 0x26 'dh', # 0x27 'n', # 0x28 -'[?]', # 0x29 +None, # 0x29 'p', # 0x2a 'ph', # 0x2b 'b', # 0x2c @@ -48,59 +48,59 @@ data = ( 'm', # 0x2e 'y', # 0x2f 'r', # 0x30 -'[?]', # 0x31 +None, # 0x31 'l', # 0x32 'll', # 0x33 -'[?]', # 0x34 +None, # 0x34 'v', # 0x35 'sh', # 0x36 -'[?]', # 0x37 +None, # 0x37 's', # 0x38 'h', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b +None, # 0x3a +None, # 0x3b '\'', # 0x3c -'[?]', # 0x3d +None, # 0x3d 'aa', # 0x3e 'i', # 0x3f 'ii', # 0x40 'u', # 0x41 'uu', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 'ee', # 0x47 'ai', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a +None, # 0x49 +None, # 0x4a 'oo', # 0x4b 'au', # 0x4c '', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 'khh', # 0x59 'ghh', # 0x5a 'z', # 0x5b 'rr', # 0x5c -'[?]', # 0x5d +None, # 0x5d 'f', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 '0', # 0x66 '1', # 0x67 '2', # 0x68 @@ -116,22 +116,22 @@ data = ( '', # 0x72 '', # 0x73 'G.E.O.', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 'N', # 0x81 'N', # 0x82 'H', # 0x83 -'[?]', # 0x84 +None, # 0x84 'a', # 0x85 'aa', # 0x86 'i', # 0x87 @@ -139,13 +139,13 @@ data = ( 'u', # 0x89 'uu', # 0x8a 'R', # 0x8b -'[?]', # 0x8c +None, # 0x8c 'eN', # 0x8d -'[?]', # 0x8e +None, # 0x8e 'e', # 0x8f 'ai', # 0x90 'oN', # 0x91 -'[?]', # 0x92 +None, # 0x92 'o', # 0x93 'au', # 0x94 'k', # 0x95 @@ -168,7 +168,7 @@ data = ( 'd', # 0xa6 'dh', # 0xa7 'n', # 0xa8 -'[?]', # 0xa9 +None, # 0xa9 'p', # 0xaa 'ph', # 0xab 'b', # 0xac @@ -176,17 +176,17 @@ data = ( 'm', # 0xae 'ya', # 0xaf 'r', # 0xb0 -'[?]', # 0xb1 +None, # 0xb1 'l', # 0xb2 'll', # 0xb3 -'[?]', # 0xb4 +None, # 0xb4 'v', # 0xb5 'sh', # 0xb6 'ss', # 0xb7 's', # 0xb8 'h', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb +None, # 0xba +None, # 0xbb '\'', # 0xbc '\'', # 0xbd 'aa', # 0xbe @@ -197,38 +197,38 @@ data = ( 'R', # 0xc3 'RR', # 0xc4 'eN', # 0xc5 -'[?]', # 0xc6 +None, # 0xc6 'e', # 0xc7 'ai', # 0xc8 'oN', # 0xc9 -'[?]', # 0xca +None, # 0xca 'o', # 0xcb 'au', # 0xcc '', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xce +None, # 0xcf 'AUM', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf 'RR', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 '0', # 0xe6 '1', # 0xe7 '2', # 0xe8 @@ -239,19 +239,19 @@ data = ( '7', # 0xed '8', # 0xee '9', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00b.py b/libs/common/unidecode/x00b.py index 19d18488..294ea45b 100644 --- a/libs/common/unidecode/x00b.py +++ b/libs/common/unidecode/x00b.py @@ -1,9 +1,9 @@ data = ( -'[?]', # 0x00 +None, # 0x00 'N', # 0x01 'N', # 0x02 'H', # 0x03 -'[?]', # 0x04 +None, # 0x04 'a', # 0x05 'aa', # 0x06 'i', # 0x07 @@ -12,12 +12,12 @@ data = ( 'uu', # 0x0a 'R', # 0x0b 'L', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e +None, # 0x0d +None, # 0x0e 'e', # 0x0f 'ai', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 +None, # 0x11 +None, # 0x12 'o', # 0x13 'au', # 0x14 'k', # 0x15 @@ -40,7 +40,7 @@ data = ( 'd', # 0x26 'dh', # 0x27 'n', # 0x28 -'[?]', # 0x29 +None, # 0x29 'p', # 0x2a 'ph', # 0x2b 'b', # 0x2c @@ -48,17 +48,17 @@ data = ( 'm', # 0x2e 'y', # 0x2f 'r', # 0x30 -'[?]', # 0x31 +None, # 0x31 'l', # 0x32 'll', # 0x33 -'[?]', # 0x34 +None, # 0x34 '', # 0x35 'sh', # 0x36 'ss', # 0x37 's', # 0x38 'h', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b +None, # 0x3a +None, # 0x3b '\'', # 0x3c '\'', # 0x3d 'aa', # 0x3e @@ -67,40 +67,40 @@ data = ( 'u', # 0x41 'uu', # 0x42 'R', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 +None, # 0x44 +None, # 0x45 +None, # 0x46 'e', # 0x47 'ai', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a +None, # 0x49 +None, # 0x4a 'o', # 0x4b 'au', # 0x4c '', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 '+', # 0x56 '+', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b 'rr', # 0x5c 'rh', # 0x5d -'[?]', # 0x5e +None, # 0x5e 'yy', # 0x5f 'RR', # 0x60 'LL', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 '0', # 0x66 '1', # 0x67 '2', # 0x68 @@ -112,67 +112,67 @@ data = ( '8', # 0x6e '9', # 0x6f '', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 'N', # 0x82 'H', # 0x83 -'[?]', # 0x84 +None, # 0x84 'a', # 0x85 'aa', # 0x86 'i', # 0x87 'ii', # 0x88 'u', # 0x89 'uu', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d +None, # 0x8b +None, # 0x8c +None, # 0x8d 'e', # 0x8e 'ee', # 0x8f 'ai', # 0x90 -'[?]', # 0x91 +None, # 0x91 'o', # 0x92 'oo', # 0x93 'au', # 0x94 'k', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 +None, # 0x96 +None, # 0x97 +None, # 0x98 'ng', # 0x99 'c', # 0x9a -'[?]', # 0x9b +None, # 0x9b 'j', # 0x9c -'[?]', # 0x9d +None, # 0x9d 'ny', # 0x9e 'tt', # 0x9f -'[?]', # 0xa0 -'[?]', # 0xa1 -'[?]', # 0xa2 +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 'nn', # 0xa3 't', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 'n', # 0xa8 'nnn', # 0xa9 'p', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad +None, # 0xab +None, # 0xac +None, # 0xad 'm', # 0xae 'y', # 0xaf 'r', # 0xb0 @@ -181,54 +181,54 @@ data = ( 'll', # 0xb3 'lll', # 0xb4 'v', # 0xb5 -'[?]', # 0xb6 +None, # 0xb6 'ss', # 0xb7 's', # 0xb8 'h', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd 'aa', # 0xbe 'i', # 0xbf 'ii', # 0xc0 'u', # 0xc1 'uu', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 'e', # 0xc6 'ee', # 0xc7 'ai', # 0xc8 -'[?]', # 0xc9 +None, # 0xc9 'o', # 0xca 'oo', # 0xcb 'au', # 0xcc '', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 '+', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 '0', # 0xe6 '1', # 0xe7 '2', # 0xe8 @@ -242,16 +242,16 @@ data = ( '+10+', # 0xf0 '+100+', # 0xf1 '+1000+', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00c.py b/libs/common/unidecode/x00c.py index 56f3654f..54987d82 100644 --- a/libs/common/unidecode/x00c.py +++ b/libs/common/unidecode/x00c.py @@ -1,9 +1,9 @@ data = ( -'[?]', # 0x00 +None, # 0x00 'N', # 0x01 'N', # 0x02 'H', # 0x03 -'[?]', # 0x04 +None, # 0x04 'a', # 0x05 'aa', # 0x06 'i', # 0x07 @@ -12,11 +12,11 @@ data = ( 'uu', # 0x0a 'R', # 0x0b 'L', # 0x0c -'[?]', # 0x0d +None, # 0x0d 'e', # 0x0e 'ee', # 0x0f 'ai', # 0x10 -'[?]', # 0x11 +None, # 0x11 'o', # 0x12 'oo', # 0x13 'au', # 0x14 @@ -40,7 +40,7 @@ data = ( 'd', # 0x26 'dh', # 0x27 'n', # 0x28 -'[?]', # 0x29 +None, # 0x29 'p', # 0x2a 'ph', # 0x2b 'b', # 0x2c @@ -51,16 +51,16 @@ data = ( 'rr', # 0x31 'l', # 0x32 'll', # 0x33 -'[?]', # 0x34 +None, # 0x34 'v', # 0x35 'sh', # 0x36 'ss', # 0x37 's', # 0x38 'h', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d 'aa', # 0x3e 'i', # 0x3f 'ii', # 0x40 @@ -68,39 +68,39 @@ data = ( 'uu', # 0x42 'R', # 0x43 'RR', # 0x44 -'[?]', # 0x45 +None, # 0x45 'e', # 0x46 'ee', # 0x47 'ai', # 0x48 -'[?]', # 0x49 +None, # 0x49 'o', # 0x4a 'oo', # 0x4b 'au', # 0x4c '', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 '+', # 0x55 '+', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f 'RR', # 0x60 'LL', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 '0', # 0x66 '1', # 0x67 '2', # 0x68 @@ -111,27 +111,27 @@ data = ( '7', # 0x6d '8', # 0x6e '9', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 'N', # 0x82 'H', # 0x83 -'[?]', # 0x84 +None, # 0x84 'a', # 0x85 'aa', # 0x86 'i', # 0x87 @@ -140,11 +140,11 @@ data = ( 'uu', # 0x8a 'R', # 0x8b 'L', # 0x8c -'[?]', # 0x8d +None, # 0x8d 'e', # 0x8e 'ee', # 0x8f 'ai', # 0x90 -'[?]', # 0x91 +None, # 0x91 'o', # 0x92 'oo', # 0x93 'au', # 0x94 @@ -168,7 +168,7 @@ data = ( 'd', # 0xa6 'dh', # 0xa7 'n', # 0xa8 -'[?]', # 0xa9 +None, # 0xa9 'p', # 0xaa 'ph', # 0xab 'b', # 0xac @@ -179,16 +179,16 @@ data = ( 'rr', # 0xb1 'l', # 0xb2 'll', # 0xb3 -'[?]', # 0xb4 +None, # 0xb4 'v', # 0xb5 'sh', # 0xb6 'ss', # 0xb7 's', # 0xb8 'h', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd 'aa', # 0xbe 'i', # 0xbf 'ii', # 0xc0 @@ -196,39 +196,39 @@ data = ( 'uu', # 0xc2 'R', # 0xc3 'RR', # 0xc4 -'[?]', # 0xc5 +None, # 0xc5 'e', # 0xc6 'ee', # 0xc7 'ai', # 0xc8 -'[?]', # 0xc9 +None, # 0xc9 'o', # 0xca 'oo', # 0xcb 'au', # 0xcc '', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 '+', # 0xd5 '+', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd 'lll', # 0xde -'[?]', # 0xdf +None, # 0xdf 'RR', # 0xe0 'LL', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 '0', # 0xe6 '1', # 0xe7 '2', # 0xe8 @@ -239,19 +239,19 @@ data = ( '7', # 0xed '8', # 0xee '9', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00d.py b/libs/common/unidecode/x00d.py index d105c437..093df92e 100644 --- a/libs/common/unidecode/x00d.py +++ b/libs/common/unidecode/x00d.py @@ -1,9 +1,9 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 +None, # 0x00 +None, # 0x01 'N', # 0x02 'H', # 0x03 -'[?]', # 0x04 +None, # 0x04 'a', # 0x05 'aa', # 0x06 'i', # 0x07 @@ -12,11 +12,11 @@ data = ( 'uu', # 0x0a 'R', # 0x0b 'L', # 0x0c -'[?]', # 0x0d +None, # 0x0d 'e', # 0x0e 'ee', # 0x0f 'ai', # 0x10 -'[?]', # 0x11 +None, # 0x11 'o', # 0x12 'oo', # 0x13 'au', # 0x14 @@ -40,7 +40,7 @@ data = ( 'd', # 0x26 'dh', # 0x27 'n', # 0x28 -'[?]', # 0x29 +None, # 0x29 'p', # 0x2a 'ph', # 0x2b 'b', # 0x2c @@ -57,18 +57,18 @@ data = ( 'ss', # 0x37 's', # 0x38 'h', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d 'aa', # 0x3e 'i', # 0x3f 'ii', # 0x40 'u', # 0x41 'uu', # 0x42 'R', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 +None, # 0x44 +None, # 0x45 'e', # 0x46 'ee', # 0x47 'ai', # 0x48 @@ -77,30 +77,30 @@ data = ( 'oo', # 0x4b 'au', # 0x4c '', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 '+', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f 'RR', # 0x60 'LL', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 '0', # 0x66 '1', # 0x67 '2', # 0x68 @@ -111,27 +111,27 @@ data = ( '7', # 0x6d '8', # 0x6e '9', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 'N', # 0x82 'H', # 0x83 -'[?]', # 0x84 +None, # 0x84 'a', # 0x85 'aa', # 0x86 'ae', # 0x87 @@ -150,9 +150,9 @@ data = ( 'o', # 0x94 'oo', # 0x95 'au', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 +None, # 0x97 +None, # 0x98 +None, # 0x99 'k', # 0x9a 'kh', # 0x9b 'g', # 0x9c @@ -177,7 +177,7 @@ data = ( 'd', # 0xaf 'dh', # 0xb0 'n', # 0xb1 -'[?]', # 0xb2 +None, # 0xb2 'nd', # 0xb3 'p', # 0xb4 'ph', # 0xb5 @@ -187,10 +187,10 @@ data = ( 'mb', # 0xb9 'y', # 0xba 'r', # 0xbb -'[?]', # 0xbc +None, # 0xbc 'l', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf +None, # 0xbe +None, # 0xbf 'v', # 0xc0 'sh', # 0xc1 'ss', # 0xc2 @@ -198,23 +198,23 @@ data = ( 'h', # 0xc4 'll', # 0xc5 'f', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 '', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce 'aa', # 0xcf 'ae', # 0xd0 'aae', # 0xd1 'i', # 0xd2 'ii', # 0xd3 'u', # 0xd4 -'[?]', # 0xd5 +None, # 0xd5 'uu', # 0xd6 -'[?]', # 0xd7 +None, # 0xd7 'R', # 0xd8 'e', # 0xd9 'ee', # 0xda @@ -223,35 +223,35 @@ data = ( 'oo', # 0xdd 'au', # 0xde 'L', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 'RR', # 0xf2 'LL', # 0xf3 ' . ', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00e.py b/libs/common/unidecode/x00e.py index 775b5f4a..5f1c085d 100644 --- a/libs/common/unidecode/x00e.py +++ b/libs/common/unidecode/x00e.py @@ -1,5 +1,5 @@ data = ( -'[?]', # 0x00 +None, # 0x00 'k', # 0x01 'kh', # 0x02 'kh', # 0x03 @@ -58,10 +58,10 @@ data = ( 'u', # 0x38 'uu', # 0x39 '\'', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e 'Bh.', # 0x3f 'e', # 0x40 'ae', # 0x41 @@ -91,67 +91,67 @@ data = ( '9', # 0x59 ' // ', # 0x5a ' /// ', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 'k', # 0x81 'kh', # 0x82 -'[?]', # 0x83 +None, # 0x83 'kh', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 +None, # 0x85 +None, # 0x86 'ng', # 0x87 'ch', # 0x88 -'[?]', # 0x89 +None, # 0x89 's', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c +None, # 0x8b +None, # 0x8c 'ny', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 'd', # 0x94 'h', # 0x95 'th', # 0x96 'th', # 0x97 -'[?]', # 0x98 +None, # 0x98 'n', # 0x99 'b', # 0x9a 'p', # 0x9b @@ -159,19 +159,19 @@ data = ( 'f', # 0x9d 'ph', # 0x9e 'f', # 0x9f -'[?]', # 0xa0 +None, # 0xa0 'm', # 0xa1 'y', # 0xa2 'r', # 0xa3 -'[?]', # 0xa4 +None, # 0xa4 'l', # 0xa5 -'[?]', # 0xa6 +None, # 0xa6 'w', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 +None, # 0xa8 +None, # 0xa9 's', # 0xaa 'h', # 0xab -'[?]', # 0xac +None, # 0xac '`', # 0xad '', # 0xae '~', # 0xaf @@ -185,28 +185,28 @@ data = ( 'yy', # 0xb7 'u', # 0xb8 'uu', # 0xb9 -'[?]', # 0xba +None, # 0xba 'o', # 0xbb 'l', # 0xbc 'ny', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf +None, # 0xbe +None, # 0xbf 'e', # 0xc0 'ei', # 0xc1 'o', # 0xc2 'ay', # 0xc3 'ai', # 0xc4 -'[?]', # 0xc5 +None, # 0xc5 '+', # 0xc6 -'[?]', # 0xc7 +None, # 0xc7 '', # 0xc8 '', # 0xc9 '', # 0xca '', # 0xcb '', # 0xcc 'M', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xce +None, # 0xcf '0', # 0xd0 '1', # 0xd1 '2', # 0xd2 @@ -217,41 +217,41 @@ data = ( '7', # 0xd7 '8', # 0xd8 '9', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb +None, # 0xda +None, # 0xdb 'hn', # 0xdc 'hm', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x00f.py b/libs/common/unidecode/x00f.py index 4c2410e0..8b66eb07 100644 --- a/libs/common/unidecode/x00f.py +++ b/libs/common/unidecode/x00f.py @@ -57,7 +57,7 @@ data = ( '_', # 0x37 '', # 0x38 '~', # 0x39 -'[?]', # 0x3a +None, # 0x3a ']', # 0x3b '[[', # 0x3c ']]', # 0x3d @@ -71,7 +71,7 @@ data = ( 'c', # 0x45 'ch', # 0x46 'j', # 0x47 -'[?]', # 0x48 +None, # 0x48 'ny', # 0x49 'tt', # 0x4a 'tth', # 0x4b @@ -106,12 +106,12 @@ data = ( 'a', # 0x68 'kss', # 0x69 'r', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 'aa', # 0x71 'i', # 0x72 'ii', # 0x73 @@ -139,10 +139,10 @@ data = ( '', # 0x89 '', # 0x8a '', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f 'k', # 0x90 'kh', # 0x91 'g', # 0x92 @@ -151,7 +151,7 @@ data = ( 'c', # 0x95 'ch', # 0x96 'j', # 0x97 -'[?]', # 0x98 +None, # 0x98 'ny', # 0x99 'tt', # 0x9a 'tth', # 0x9b @@ -188,7 +188,7 @@ data = ( 'w', # 0xba 'y', # 0xbb 'r', # 0xbc -'[?]', # 0xbd +None, # 0xbd 'X', # 0xbe ' :X: ', # 0xbf ' /O/ ', # 0xc0 @@ -204,54 +204,54 @@ data = ( '', # 0xca '', # 0xcb '', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce +None, # 0xcd +None, # 0xce '', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x010.py b/libs/common/unidecode/x010.py index aaf820d9..7f9ef764 100644 --- a/libs/common/unidecode/x010.py +++ b/libs/common/unidecode/x010.py @@ -33,16 +33,16 @@ data = ( 'h', # 0x1f 'll', # 0x20 'a', # 0x21 -'[?]', # 0x22 +None, # 0x22 'i', # 0x23 'ii', # 0x24 'u', # 0x25 'uu', # 0x26 'e', # 0x27 -'[?]', # 0x28 +None, # 0x28 'o', # 0x29 'au', # 0x2a -'[?]', # 0x2b +None, # 0x2b 'aa', # 0x2c 'i', # 0x2d 'ii', # 0x2e @@ -50,19 +50,19 @@ data = ( 'uu', # 0x30 'e', # 0x31 'ai', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 +None, # 0x33 +None, # 0x34 +None, # 0x35 'N', # 0x36 '\'', # 0x37 ':', # 0x38 '', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f '0', # 0x40 '1', # 0x41 '2', # 0x42 @@ -89,76 +89,76 @@ data = ( 'RR', # 0x57 'L', # 0x58 'LL', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f 'A', # 0xa0 'B', # 0xa1 'G', # 0xa2 @@ -197,16 +197,16 @@ data = ( 'W', # 0xc3 'Xh', # 0xc4 'OE', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf 'a', # 0xd0 'b', # 0xd1 'g', # 0xd2 @@ -246,12 +246,12 @@ data = ( 'xh', # 0xf4 'oe', # 0xf5 'f', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa ' // ', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x011.py b/libs/common/unidecode/x011.py index f0d8f929..51bfb0a0 100644 --- a/libs/common/unidecode/x011.py +++ b/libs/common/unidecode/x011.py @@ -89,11 +89,11 @@ data = ( 'pN', # 0x57 'hh', # 0x58 'Q', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e '', # 0x5f '', # 0x60 'a', # 0x61 @@ -162,11 +162,11 @@ data = ( 'U-u', # 0xa0 'U-i', # 0xa1 'UU', # 0xa2 -'[?]', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 'g', # 0xa8 'gg', # 0xa9 'gs', # 0xaa @@ -249,9 +249,9 @@ data = ( 'hm', # 0xf7 'hb', # 0xf8 'Q', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x012.py b/libs/common/unidecode/x012.py index f2670650..2314a559 100644 --- a/libs/common/unidecode/x012.py +++ b/libs/common/unidecode/x012.py @@ -6,7 +6,7 @@ data = ( 'hee', # 0x04 'he', # 0x05 'ho', # 0x06 -'[?]', # 0x07 +None, # 0x07 'la', # 0x08 'lu', # 0x09 'li', # 0x0a @@ -70,15 +70,15 @@ data = ( 'qee', # 0x44 'qe', # 0x45 'qo', # 0x46 -'[?]', # 0x47 +None, # 0x47 'qwa', # 0x48 -'[?]', # 0x49 +None, # 0x49 'qwi', # 0x4a 'qwaa', # 0x4b 'qwee', # 0x4c 'qwe', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f +None, # 0x4e +None, # 0x4f 'qha', # 0x50 'qhu', # 0x51 'qhi', # 0x52 @@ -86,15 +86,15 @@ data = ( 'qhee', # 0x54 'qhe', # 0x55 'qho', # 0x56 -'[?]', # 0x57 +None, # 0x57 'qhwa', # 0x58 -'[?]', # 0x59 +None, # 0x59 'qhwi', # 0x5a 'qhwaa', # 0x5b 'qhwee', # 0x5c 'qhwe', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x5e +None, # 0x5f 'ba', # 0x60 'bu', # 0x61 'bi', # 0x62 @@ -134,15 +134,15 @@ data = ( 'xee', # 0x84 'xe', # 0x85 'xo', # 0x86 -'[?]', # 0x87 +None, # 0x87 'xwa', # 0x88 -'[?]', # 0x89 +None, # 0x89 'xwi', # 0x8a 'xwaa', # 0x8b 'xwee', # 0x8c 'xwe', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f +None, # 0x8e +None, # 0x8f 'na', # 0x90 'nu', # 0x91 'ni', # 0x92 @@ -161,7 +161,7 @@ data = ( 'nywa', # 0x9f '\'a', # 0xa0 '\'u', # 0xa1 -'[?]', # 0xa2 +None, # 0xa2 '\'aa', # 0xa3 '\'ee', # 0xa4 '\'e', # 0xa5 @@ -174,15 +174,15 @@ data = ( 'kee', # 0xac 'ke', # 0xad 'ko', # 0xae -'[?]', # 0xaf +None, # 0xaf 'kwa', # 0xb0 -'[?]', # 0xb1 +None, # 0xb1 'kwi', # 0xb2 'kwaa', # 0xb3 'kwee', # 0xb4 'kwe', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 +None, # 0xb6 +None, # 0xb7 'kxa', # 0xb8 'kxu', # 0xb9 'kxi', # 0xba @@ -190,15 +190,15 @@ data = ( 'kxee', # 0xbc 'kxe', # 0xbd 'kxo', # 0xbe -'[?]', # 0xbf +None, # 0xbf 'kxwa', # 0xc0 -'[?]', # 0xc1 +None, # 0xc1 'kxwi', # 0xc2 'kxwaa', # 0xc3 'kxwee', # 0xc4 'kxwe', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 +None, # 0xc6 +None, # 0xc7 'wa', # 0xc8 'wu', # 0xc9 'wi', # 0xca @@ -206,7 +206,7 @@ data = ( 'wee', # 0xcc 'we', # 0xcd 'wo', # 0xce -'[?]', # 0xcf +None, # 0xcf '`a', # 0xd0 '`u', # 0xd1 '`i', # 0xd2 @@ -214,7 +214,7 @@ data = ( '`ee', # 0xd4 '`e', # 0xd5 '`o', # 0xd6 -'[?]', # 0xd7 +None, # 0xd7 'za', # 0xd8 'zu', # 0xd9 'zi', # 0xda @@ -238,7 +238,7 @@ data = ( 'yee', # 0xec 'ye', # 0xed 'yo', # 0xee -'[?]', # 0xef +None, # 0xef 'da', # 0xf0 'du', # 0xf1 'di', # 0xf2 diff --git a/libs/common/unidecode/x013.py b/libs/common/unidecode/x013.py index 8a8c3f9c..f1e4b997 100644 --- a/libs/common/unidecode/x013.py +++ b/libs/common/unidecode/x013.py @@ -14,15 +14,15 @@ data = ( 'gee', # 0x0c 'ge', # 0x0d 'go', # 0x0e -'[?]', # 0x0f +None, # 0x0f 'gwa', # 0x10 -'[?]', # 0x11 +None, # 0x11 'gwi', # 0x12 'gwaa', # 0x13 'gwee', # 0x14 'gwe', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 +None, # 0x16 +None, # 0x17 'gga', # 0x18 'ggu', # 0x19 'ggi', # 0x1a @@ -30,7 +30,7 @@ data = ( 'ggee', # 0x1c 'gge', # 0x1d 'ggo', # 0x1e -'[?]', # 0x1f +None, # 0x1f 'tha', # 0x20 'thu', # 0x21 'thi', # 0x22 @@ -70,7 +70,7 @@ data = ( 'tzee', # 0x44 'tze', # 0x45 'tzo', # 0x46 -'[?]', # 0x47 +None, # 0x47 'fa', # 0x48 'fu', # 0x49 'fi', # 0x4a @@ -90,12 +90,12 @@ data = ( 'rya', # 0x58 'mya', # 0x59 'fya', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 ' ', # 0x61 '.', # 0x62 ',', # 0x63 @@ -124,41 +124,41 @@ data = ( '90+', # 0x7a '100+', # 0x7b '10,000+', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f 'a', # 0xa0 'e', # 0xa1 'i', # 0xa2 @@ -244,14 +244,14 @@ data = ( 'yo', # 0xf2 'yu', # 0xf3 'yv', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x014.py b/libs/common/unidecode/x014.py index e8c01809..0f56a91f 100644 --- a/libs/common/unidecode/x014.py +++ b/libs/common/unidecode/x014.py @@ -1,5 +1,5 @@ data = ( -'[?]', # 0x00 +None, # 0x00 'e', # 0x01 'aai', # 0x02 'i', # 0x03 @@ -37,7 +37,7 @@ data = ( 'n', # 0x23 'w', # 0x24 'n', # 0x25 -'[?]', # 0x26 +None, # 0x26 'w', # 0x27 'c', # 0x28 '?', # 0x29 diff --git a/libs/common/unidecode/x016.py b/libs/common/unidecode/x016.py index 613d1e90..0998689d 100644 --- a/libs/common/unidecode/x016.py +++ b/libs/common/unidecode/x016.py @@ -118,15 +118,15 @@ data = ( 'nngoo', # 0x74 'nnga', # 0x75 'nngaa', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f ' ', # 0x80 'b', # 0x81 'l', # 0x82 @@ -156,9 +156,9 @@ data = ( 'p', # 0x9a '<', # 0x9b '>', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f +None, # 0x9d +None, # 0x9e +None, # 0x9f 'f', # 0xa0 'v', # 0xa1 'u', # 0xa2 @@ -240,18 +240,18 @@ data = ( '17', # 0xee '18', # 0xef '19', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x017.py b/libs/common/unidecode/x017.py index e0a8f447..09ca04c2 100644 --- a/libs/common/unidecode/x017.py +++ b/libs/common/unidecode/x017.py @@ -1,132 +1,132 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f -'[?]', # 0x20 -'[?]', # 0x21 -'[?]', # 0x22 -'[?]', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 -'[?]', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f 'k', # 0x80 'kh', # 0x81 'g', # 0x82 @@ -220,9 +220,9 @@ data = ( ' /// ', # 0xda 'KR', # 0xdb '\'', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf +None, # 0xdd +None, # 0xde +None, # 0xdf '0', # 0xe0 '1', # 0xe1 '2', # 0xe2 @@ -233,25 +233,25 @@ data = ( '7', # 0xe7 '8', # 0xe8 '9', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x018.py b/libs/common/unidecode/x018.py index 3162a010..1b52c8aa 100644 --- a/libs/common/unidecode/x018.py +++ b/libs/common/unidecode/x018.py @@ -14,7 +14,7 @@ data = ( '', # 0x0c '', # 0x0d '', # 0x0e -'[?]', # 0x0f +None, # 0x0f '0', # 0x10 '1', # 0x11 '2', # 0x12 @@ -25,12 +25,12 @@ data = ( '7', # 0x17 '8', # 0x18 '9', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f 'a', # 0x20 'e', # 0x21 'i', # 0x22 @@ -119,15 +119,15 @@ data = ( 'r', # 0x75 'f', # 0x76 'zh', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 'H', # 0x81 'X', # 0x82 'W', # 0x83 @@ -169,89 +169,89 @@ data = ( 'y', # 0xa7 'bh', # 0xa8 '\'', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x01e.py b/libs/common/unidecode/x01e.py index 606576b6..540786a8 100644 --- a/libs/common/unidecode/x01e.py +++ b/libs/common/unidecode/x01e.py @@ -155,10 +155,10 @@ data = ( 'y', # 0x99 'a', # 0x9a 'S', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d +None, # 0x9c +None, # 0x9d 'Ss', # 0x9e -'[?]', # 0x9f +None, # 0x9f 'A', # 0xa0 'a', # 0xa1 'A', # 0xa2 @@ -249,9 +249,9 @@ data = ( 'y', # 0xf7 'Y', # 0xf8 'y', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x01f.py b/libs/common/unidecode/x01f.py index bcd2dec5..f65fc6bc 100644 --- a/libs/common/unidecode/x01f.py +++ b/libs/common/unidecode/x01f.py @@ -21,16 +21,16 @@ data = ( 'e', # 0x13 'e', # 0x14 'e', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 +None, # 0x16 +None, # 0x17 'E', # 0x18 'E', # 0x19 'E', # 0x1a 'E', # 0x1b 'E', # 0x1c 'E', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f +None, # 0x1e +None, # 0x1f 'e', # 0x20 'e', # 0x21 'e', # 0x22 @@ -69,16 +69,16 @@ data = ( 'o', # 0x43 'o', # 0x44 'o', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 +None, # 0x46 +None, # 0x47 'O', # 0x48 'O', # 0x49 'O', # 0x4a 'O', # 0x4b 'O', # 0x4c 'O', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f +None, # 0x4e +None, # 0x4f 'u', # 0x50 'u', # 0x51 'u', # 0x52 @@ -87,13 +87,13 @@ data = ( 'u', # 0x55 'u', # 0x56 'u', # 0x57 -'[?]', # 0x58 +None, # 0x58 'U', # 0x59 -'[?]', # 0x5a +None, # 0x5a 'U', # 0x5b -'[?]', # 0x5c +None, # 0x5c 'U', # 0x5d -'[?]', # 0x5e +None, # 0x5e 'U', # 0x5f 'o', # 0x60 'o', # 0x61 @@ -125,8 +125,8 @@ data = ( 'u', # 0x7b 'o', # 0x7c 'o', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f +None, # 0x7e +None, # 0x7f 'a', # 0x80 'a', # 0x81 'a', # 0x82 @@ -180,7 +180,7 @@ data = ( 'a', # 0xb2 'a', # 0xb3 'a', # 0xb4 -'[?]', # 0xb5 +None, # 0xb5 'a', # 0xb6 'a', # 0xb7 'A', # 0xb8 @@ -196,7 +196,7 @@ data = ( 'e', # 0xc2 'e', # 0xc3 'e', # 0xc4 -'[?]', # 0xc5 +None, # 0xc5 'e', # 0xc6 'e', # 0xc7 'E', # 0xc8 @@ -211,15 +211,15 @@ data = ( 'i', # 0xd1 'i', # 0xd2 'i', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 +None, # 0xd4 +None, # 0xd5 'i', # 0xd6 'i', # 0xd7 'I', # 0xd8 'I', # 0xd9 'I', # 0xda 'I', # 0xdb -'[?]', # 0xdc +None, # 0xdc '`\'', # 0xdd '`\'', # 0xde '`~', # 0xdf @@ -239,12 +239,12 @@ data = ( '"`', # 0xed '"\'', # 0xee '`', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 +None, # 0xf0 +None, # 0xf1 'o', # 0xf2 'o', # 0xf3 'o', # 0xf4 -'[?]', # 0xf5 +None, # 0xf5 'o', # 0xf6 'o', # 0xf7 'O', # 0xf8 diff --git a/libs/common/unidecode/x020.py b/libs/common/unidecode/x020.py index 46425bf0..60ff9616 100644 --- a/libs/common/unidecode/x020.py +++ b/libs/common/unidecode/x020.py @@ -73,38 +73,42 @@ data = ( '??', # 0x47 '?!', # 0x48 '!?', # 0x49 -'7', # 0x4a + +# Tironian note standing for Latin "et". Still used as an ampersand +# in modern Irish. See https://github.com/avian2/unidecode/issues/57 +'&', # 0x4a + 'PP', # 0x4b '(]', # 0x4c '[)', # 0x4d '*', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 +None, # 0x4f +None, # 0x50 +None, # 0x51 '%', # 0x52 '~', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 +None, # 0x54 +None, # 0x55 +None, # 0x56 "''''", # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e ' ', # 0x5f '', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 '', # 0x6a '', # 0x6b '', # 0x6c @@ -142,12 +146,12 @@ data = ( '=', # 0x8c '(', # 0x8d ')', # 0x8e -'[?]', # 0x8f +None, # 0x8f 'a', # 0x90 'e', # 0x91 'o', # 0x92 'x', # 0x93 -'[?]', # 0x94 +None, # 0x94 'h', # 0x95 'k', # 0x96 'l', # 0x97 @@ -156,9 +160,9 @@ data = ( 'p', # 0x9a 's', # 0x9b 't', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f +None, # 0x9d +None, # 0x9e +None, # 0x9f 'ECU', # 0xa0 'CL', # 0xa1 'Cr', # 0xa2 @@ -191,22 +195,22 @@ data = ( 'R', # 0xbd 'l', # 0xbe 'BTC', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf '', # 0xd0 '', # 0xd1 '', # 0xd2 @@ -227,31 +231,31 @@ data = ( '', # 0xe1 '', # 0xe2 '', # 0xe3 -'[?]', # 0xe4 +None, # 0xe4 '', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x021.py b/libs/common/unidecode/x021.py index 29f05fd4..6bd37f7e 100644 --- a/libs/common/unidecode/x021.py +++ b/libs/common/unidecode/x021.py @@ -22,7 +22,7 @@ data = ( '', # 0x14 'N', # 0x15 'No. ', # 0x16 -'', # 0x17 +'(p)', # 0x17 '', # 0x18 'P', # 0x19 'Q', # 0x1a @@ -37,7 +37,7 @@ data = ( '', # 0x23 'Z', # 0x24 '', # 0x25 -'', # 0x26 +'ohm', # 0x26 '', # 0x27 'Z', # 0x28 '', # 0x29 @@ -63,22 +63,22 @@ data = ( '', # 0x3d '', # 0x3e '', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 'D', # 0x45 'd', # 0x46 'e', # 0x47 'i', # 0x48 'j', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d 'F', # 0x4e -'[?]', # 0x4f +None, # 0x4f ' 1/7 ', # 0x50 ' 1/9 ', # 0x51 ' 1/10 ', # 0x52 @@ -131,18 +131,18 @@ data = ( 'D)', # 0x81 '((|))', # 0x82 ')', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 ' 0/3 ', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f '-', # 0x90 '|', # 0x91 '-', # 0x92 @@ -243,15 +243,15 @@ data = ( '\\', # 0xf1 '\\', # 0xf2 '|', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x022.py b/libs/common/unidecode/x022.py index e38fb5cc..f2db33f0 100644 --- a/libs/common/unidecode/x022.py +++ b/libs/common/unidecode/x022.py @@ -1,257 +1,257 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 '-', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 +None, # 0x13 +None, # 0x14 '/', # 0x15 '\\', # 0x16 '*', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f -'[?]', # 0x20 -'[?]', # 0x21 -'[?]', # 0x22 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 '|', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 ':', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b '~', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 '<=', # 0x64 '>=', # 0x65 '<=', # 0x66 '>=', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f -'[?]', # 0xa0 -'[?]', # 0xa1 -'[?]', # 0xa2 -'[?]', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x023.py b/libs/common/unidecode/x023.py index 3c4462e2..cb798848 100644 --- a/libs/common/unidecode/x023.py +++ b/libs/common/unidecode/x023.py @@ -1,257 +1,257 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 +None, # 0x00 +None, # 0x01 +None, # 0x02 '^', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f -'[?]', # 0x20 -'[?]', # 0x21 -'[?]', # 0x22 -'[?]', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 '<', # 0x29 '> ', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 -'[?]', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f -'[?]', # 0xa0 -'[?]', # 0xa1 -'[?]', # 0xa2 -'[?]', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x024.py b/libs/common/unidecode/x024.py index 231b0ca1..57126178 100644 --- a/libs/common/unidecode/x024.py +++ b/libs/common/unidecode/x024.py @@ -38,31 +38,31 @@ data = ( '', # 0x24 '', # 0x25 '', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 -'[?]', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f '', # 0x40 '', # 0x41 '', # 0x42 @@ -74,27 +74,27 @@ data = ( '', # 0x48 '', # 0x49 '', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f '1', # 0x60 '2', # 0x61 '3', # 0x62 diff --git a/libs/common/unidecode/x025.py b/libs/common/unidecode/x025.py index 5a62b10d..90eac33b 100644 --- a/libs/common/unidecode/x025.py +++ b/libs/common/unidecode/x025.py @@ -149,16 +149,16 @@ data = ( '#', # 0x93 '-', # 0x94 '|', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f '#', # 0xa0 '#', # 0xa1 '#', # 0xa2 @@ -247,11 +247,11 @@ data = ( '#', # 0xf5 '#', # 0xf6 '#', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x026.py b/libs/common/unidecode/x026.py index c575472c..380e84b9 100644 --- a/libs/common/unidecode/x026.py +++ b/libs/common/unidecode/x026.py @@ -19,11 +19,11 @@ data = ( '', # 0x11 '', # 0x12 '', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 '', # 0x19 '', # 0x1a '', # 0x1b @@ -113,145 +113,145 @@ data = ( '#', # 0x6f '', # 0x70 '', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f -'[?]', # 0xa0 -'[?]', # 0xa1 -'[?]', # 0xa2 -'[?]', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x027.py b/libs/common/unidecode/x027.py index 3c74c073..ab33ab95 100644 --- a/libs/common/unidecode/x027.py +++ b/libs/common/unidecode/x027.py @@ -1,5 +1,5 @@ data = ( -'[?]', # 0x00 +None, # 0x00 '', # 0x01 '', # 0x02 '', # 0x03 @@ -91,11 +91,11 @@ data = ( '', # 0x59 '', # 0x5a '', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 +'\'', # 0x5c +'"', # 0x5d +'"', # 0x5e +',', # 0x5f +',,', # 0x60 '', # 0x61 '!', # 0x62 '', # 0x63 @@ -175,7 +175,7 @@ data = ( '', # 0xad '', # 0xae '', # 0xaf -'[?]', # 0xb0 +None, # 0xb0 '', # 0xb1 '', # 0xb2 '', # 0xb3 @@ -190,68 +190,68 @@ data = ( '', # 0xbc '', # 0xbd '', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 '[', # 0xe6 -'[?]', # 0xe7 +None, # 0xe7 '<', # 0xe8 '> ', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x029.py b/libs/common/unidecode/x029.py index c2df2548..717e9031 100644 --- a/libs/common/unidecode/x029.py +++ b/libs/common/unidecode/x029.py @@ -1,257 +1,257 @@ data = ( -'', # 0x00 -'', # 0x01 -'', # 0x02 -'', # 0x03 -'', # 0x04 -'', # 0x05 -'', # 0x06 -'', # 0x07 -'', # 0x08 -'', # 0x09 -'', # 0x0a -'', # 0x0b -'', # 0x0c -'', # 0x0d -'', # 0x0e -'', # 0x0f -'', # 0x10 -'', # 0x11 -'', # 0x12 -'', # 0x13 -'', # 0x14 -'', # 0x15 -'', # 0x16 -'', # 0x17 -'', # 0x18 -'', # 0x19 -'', # 0x1a -'', # 0x1b -'', # 0x1c -'', # 0x1d -'', # 0x1e -'', # 0x1f -'', # 0x20 -'', # 0x21 -'', # 0x22 -'', # 0x23 -'', # 0x24 -'', # 0x25 -'', # 0x26 -'', # 0x27 -'', # 0x28 -'', # 0x29 -'', # 0x2a -'', # 0x2b -'', # 0x2c -'', # 0x2d -'', # 0x2e -'', # 0x2f -'', # 0x30 -'', # 0x31 -'', # 0x32 -'', # 0x33 -'', # 0x34 -'', # 0x35 -'', # 0x36 -'', # 0x37 -'', # 0x38 -'', # 0x39 -'', # 0x3a -'', # 0x3b -'', # 0x3c -'', # 0x3d -'', # 0x3e -'', # 0x3f -'', # 0x40 -'', # 0x41 -'', # 0x42 -'', # 0x43 -'', # 0x44 -'', # 0x45 -'', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 -'', # 0x4a -'', # 0x4b -'', # 0x4c -'', # 0x4d -'', # 0x4e -'', # 0x4f -'', # 0x50 -'', # 0x51 -'', # 0x52 -'', # 0x53 -'', # 0x54 -'', # 0x55 -'', # 0x56 -'', # 0x57 -'', # 0x58 -'', # 0x59 -'', # 0x5a -'', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'', # 0x5f -'', # 0x60 -'', # 0x61 -'', # 0x62 -'', # 0x63 -'', # 0x64 -'', # 0x65 -'', # 0x66 -'', # 0x67 -'', # 0x68 -'', # 0x69 -'', # 0x6a -'', # 0x6b -'', # 0x6c -'', # 0x6d -'', # 0x6e -'', # 0x6f -'', # 0x70 -'', # 0x71 -'', # 0x72 -'', # 0x73 -'', # 0x74 -'', # 0x75 -'', # 0x76 -'', # 0x77 -'', # 0x78 -'', # 0x79 -'', # 0x7a -'', # 0x7b -'', # 0x7c -'', # 0x7d -'', # 0x7e -'', # 0x7f -'', # 0x80 -'', # 0x81 -'', # 0x82 +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 '{', # 0x83 '} ', # 0x84 -'', # 0x85 -'', # 0x86 -'', # 0x87 -'', # 0x88 -'', # 0x89 -'', # 0x8a -'', # 0x8b -'', # 0x8c -'', # 0x8d -'', # 0x8e -'', # 0x8f -'', # 0x90 -'', # 0x91 -'', # 0x92 -'', # 0x93 -'', # 0x94 -'', # 0x95 -'', # 0x96 -'', # 0x97 -'', # 0x98 -'', # 0x99 -'', # 0x9a -'', # 0x9b -'', # 0x9c -'', # 0x9d -'', # 0x9e -'', # 0x9f -'', # 0xa0 -'', # 0xa1 -'', # 0xa2 -'', # 0xa3 -'', # 0xa4 -'', # 0xa5 -'', # 0xa6 -'', # 0xa7 -'', # 0xa8 -'', # 0xa9 -'', # 0xaa -'', # 0xab -'', # 0xac -'', # 0xad -'', # 0xae -'', # 0xaf -'', # 0xb0 -'', # 0xb1 -'', # 0xb2 -'', # 0xb3 -'', # 0xb4 -'', # 0xb5 -'', # 0xb6 -'', # 0xb7 -'', # 0xb8 -'', # 0xb9 -'', # 0xba -'', # 0xbb -'', # 0xbc -'', # 0xbd -'', # 0xbe -'', # 0xbf -'', # 0xc0 -'', # 0xc1 -'', # 0xc2 -'', # 0xc3 -'', # 0xc4 -'', # 0xc5 -'', # 0xc6 -'', # 0xc7 -'', # 0xc8 -'', # 0xc9 -'', # 0xca -'', # 0xcb -'', # 0xcc -'', # 0xcd -'', # 0xce -'', # 0xcf -'', # 0xd0 -'', # 0xd1 -'', # 0xd2 -'', # 0xd3 -'', # 0xd4 -'', # 0xd5 -'', # 0xd6 -'', # 0xd7 -'', # 0xd8 -'', # 0xd9 -'', # 0xda -'', # 0xdb -'', # 0xdc -'', # 0xdd -'', # 0xde -'', # 0xdf -'', # 0xe0 -'', # 0xe1 -'', # 0xe2 -'', # 0xe3 -'', # 0xe4 -'', # 0xe5 -'', # 0xe6 -'', # 0xe7 -'', # 0xe8 -'', # 0xe9 -'', # 0xea -'', # 0xeb -'', # 0xec -'', # 0xed -'', # 0xee -'', # 0xef -'', # 0xf0 -'', # 0xf1 -'', # 0xf2 -'', # 0xf3 -'', # 0xf4 -'', # 0xf5 -'', # 0xf6 -'', # 0xf7 -'', # 0xf8 -'', # 0xf9 -'', # 0xfa -'', # 0xfb -'', # 0xfc -'', # 0xfd -'', # 0xfe +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x02a.py b/libs/common/unidecode/x02a.py index b832ef35..4f574c14 100644 --- a/libs/common/unidecode/x02a.py +++ b/libs/common/unidecode/x02a.py @@ -1,257 +1,257 @@ data = ( -'', # 0x00 -'', # 0x01 -'', # 0x02 -'', # 0x03 -'', # 0x04 -'', # 0x05 -'', # 0x06 -'', # 0x07 -'', # 0x08 -'', # 0x09 -'', # 0x0a -'', # 0x0b -'', # 0x0c -'', # 0x0d -'', # 0x0e -'', # 0x0f -'', # 0x10 -'', # 0x11 -'', # 0x12 -'', # 0x13 -'', # 0x14 -'', # 0x15 -'', # 0x16 -'', # 0x17 -'', # 0x18 -'', # 0x19 -'', # 0x1a -'', # 0x1b -'', # 0x1c -'', # 0x1d -'', # 0x1e -'', # 0x1f -'', # 0x20 -'', # 0x21 -'', # 0x22 -'', # 0x23 -'', # 0x24 -'', # 0x25 -'', # 0x26 -'', # 0x27 -'', # 0x28 -'', # 0x29 -'', # 0x2a -'', # 0x2b -'', # 0x2c -'', # 0x2d -'', # 0x2e -'', # 0x2f -'', # 0x30 -'', # 0x31 -'', # 0x32 -'', # 0x33 -'', # 0x34 -'', # 0x35 -'', # 0x36 -'', # 0x37 -'', # 0x38 -'', # 0x39 -'', # 0x3a -'', # 0x3b -'', # 0x3c -'', # 0x3d -'', # 0x3e -'', # 0x3f -'', # 0x40 -'', # 0x41 -'', # 0x42 -'', # 0x43 -'', # 0x44 -'', # 0x45 -'', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 -'', # 0x4a -'', # 0x4b -'', # 0x4c -'', # 0x4d -'', # 0x4e -'', # 0x4f -'', # 0x50 -'', # 0x51 -'', # 0x52 -'', # 0x53 -'', # 0x54 -'', # 0x55 -'', # 0x56 -'', # 0x57 -'', # 0x58 -'', # 0x59 -'', # 0x5a -'', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'', # 0x5f -'', # 0x60 -'', # 0x61 -'', # 0x62 -'', # 0x63 -'', # 0x64 -'', # 0x65 -'', # 0x66 -'', # 0x67 -'', # 0x68 -'', # 0x69 -'', # 0x6a -'', # 0x6b -'', # 0x6c -'', # 0x6d -'', # 0x6e -'', # 0x6f -'', # 0x70 -'', # 0x71 -'', # 0x72 -'', # 0x73 +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 '::=', # 0x74 '==', # 0x75 '===', # 0x76 -'', # 0x77 -'', # 0x78 -'', # 0x79 -'', # 0x7a -'', # 0x7b -'', # 0x7c -'', # 0x7d -'', # 0x7e -'', # 0x7f -'', # 0x80 -'', # 0x81 -'', # 0x82 -'', # 0x83 -'', # 0x84 -'', # 0x85 -'', # 0x86 -'', # 0x87 -'', # 0x88 -'', # 0x89 -'', # 0x8a -'', # 0x8b -'', # 0x8c -'', # 0x8d -'', # 0x8e -'', # 0x8f -'', # 0x90 -'', # 0x91 -'', # 0x92 -'', # 0x93 -'', # 0x94 -'', # 0x95 -'', # 0x96 -'', # 0x97 -'', # 0x98 -'', # 0x99 -'', # 0x9a -'', # 0x9b -'', # 0x9c -'', # 0x9d -'', # 0x9e -'', # 0x9f -'', # 0xa0 -'', # 0xa1 -'', # 0xa2 -'', # 0xa3 -'', # 0xa4 -'', # 0xa5 -'', # 0xa6 -'', # 0xa7 -'', # 0xa8 -'', # 0xa9 -'', # 0xaa -'', # 0xab -'', # 0xac -'', # 0xad -'', # 0xae -'', # 0xaf -'', # 0xb0 -'', # 0xb1 -'', # 0xb2 -'', # 0xb3 -'', # 0xb4 -'', # 0xb5 -'', # 0xb6 -'', # 0xb7 -'', # 0xb8 -'', # 0xb9 -'', # 0xba -'', # 0xbb -'', # 0xbc -'', # 0xbd -'', # 0xbe -'', # 0xbf -'', # 0xc0 -'', # 0xc1 -'', # 0xc2 -'', # 0xc3 -'', # 0xc4 -'', # 0xc5 -'', # 0xc6 -'', # 0xc7 -'', # 0xc8 -'', # 0xc9 -'', # 0xca -'', # 0xcb -'', # 0xcc -'', # 0xcd -'', # 0xce -'', # 0xcf -'', # 0xd0 -'', # 0xd1 -'', # 0xd2 -'', # 0xd3 -'', # 0xd4 -'', # 0xd5 -'', # 0xd6 -'', # 0xd7 -'', # 0xd8 -'', # 0xd9 -'', # 0xda -'', # 0xdb -'', # 0xdc -'', # 0xdd -'', # 0xde -'', # 0xdf -'', # 0xe0 -'', # 0xe1 -'', # 0xe2 -'', # 0xe3 -'', # 0xe4 -'', # 0xe5 -'', # 0xe6 -'', # 0xe7 -'', # 0xe8 -'', # 0xe9 -'', # 0xea -'', # 0xeb -'', # 0xec -'', # 0xed -'', # 0xee -'', # 0xef -'', # 0xf0 -'', # 0xf1 -'', # 0xf2 -'', # 0xf3 -'', # 0xf4 -'', # 0xf5 -'', # 0xf6 -'', # 0xf7 -'', # 0xf8 -'', # 0xf9 -'', # 0xfa -'', # 0xfb -'', # 0xfc -'', # 0xfd -'', # 0xfe +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x02c.py b/libs/common/unidecode/x02c.py index 0d05d069..f6e6d1f3 100644 --- a/libs/common/unidecode/x02c.py +++ b/libs/common/unidecode/x02c.py @@ -1,100 +1,100 @@ data = ( -'', # 0x00 -'', # 0x01 -'', # 0x02 -'', # 0x03 -'', # 0x04 -'', # 0x05 -'', # 0x06 -'', # 0x07 -'', # 0x08 -'', # 0x09 -'', # 0x0a -'', # 0x0b -'', # 0x0c -'', # 0x0d -'', # 0x0e -'', # 0x0f -'', # 0x10 -'', # 0x11 -'', # 0x12 -'', # 0x13 -'', # 0x14 -'', # 0x15 -'', # 0x16 -'', # 0x17 -'', # 0x18 -'', # 0x19 -'', # 0x1a -'', # 0x1b -'', # 0x1c -'', # 0x1d -'', # 0x1e -'', # 0x1f -'', # 0x20 -'', # 0x21 -'', # 0x22 -'', # 0x23 -'', # 0x24 -'', # 0x25 -'', # 0x26 -'', # 0x27 -'', # 0x28 -'', # 0x29 -'', # 0x2a -'', # 0x2b -'', # 0x2c -'', # 0x2d -'', # 0x2e -'', # 0x2f -'', # 0x30 -'', # 0x31 -'', # 0x32 -'', # 0x33 -'', # 0x34 -'', # 0x35 -'', # 0x36 -'', # 0x37 -'', # 0x38 -'', # 0x39 -'', # 0x3a -'', # 0x3b -'', # 0x3c -'', # 0x3d -'', # 0x3e -'', # 0x3f -'', # 0x40 -'', # 0x41 -'', # 0x42 -'', # 0x43 -'', # 0x44 -'', # 0x45 -'', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 -'', # 0x4a -'', # 0x4b -'', # 0x4c -'', # 0x4d -'', # 0x4e -'', # 0x4f -'', # 0x50 -'', # 0x51 -'', # 0x52 -'', # 0x53 -'', # 0x54 -'', # 0x55 -'', # 0x56 -'', # 0x57 -'', # 0x58 -'', # 0x59 -'', # 0x5a -'', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'', # 0x5f +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f 'L', # 0x60 'l', # 0x61 'L', # 0x62 @@ -108,150 +108,150 @@ data = ( 'k', # 0x6a 'Z', # 0x6b 'z', # 0x6c -'', # 0x6d +None, # 0x6d 'M', # 0x6e 'A', # 0x6f -'', # 0x70 -'', # 0x71 -'', # 0x72 -'', # 0x73 -'', # 0x74 -'', # 0x75 -'', # 0x76 -'', # 0x77 -'', # 0x78 -'', # 0x79 -'', # 0x7a -'', # 0x7b -'', # 0x7c -'', # 0x7d -'', # 0x7e -'', # 0x7f -'', # 0x80 -'', # 0x81 -'', # 0x82 -'', # 0x83 -'', # 0x84 -'', # 0x85 -'', # 0x86 -'', # 0x87 -'', # 0x88 -'', # 0x89 -'', # 0x8a -'', # 0x8b -'', # 0x8c -'', # 0x8d -'', # 0x8e -'', # 0x8f -'', # 0x90 -'', # 0x91 -'', # 0x92 -'', # 0x93 -'', # 0x94 -'', # 0x95 -'', # 0x96 -'', # 0x97 -'', # 0x98 -'', # 0x99 -'', # 0x9a -'', # 0x9b -'', # 0x9c -'', # 0x9d -'', # 0x9e -'', # 0x9f -'', # 0xa0 -'', # 0xa1 -'', # 0xa2 -'', # 0xa3 -'', # 0xa4 -'', # 0xa5 -'', # 0xa6 -'', # 0xa7 -'', # 0xa8 -'', # 0xa9 -'', # 0xaa -'', # 0xab -'', # 0xac -'', # 0xad -'', # 0xae -'', # 0xaf -'', # 0xb0 -'', # 0xb1 -'', # 0xb2 -'', # 0xb3 -'', # 0xb4 -'', # 0xb5 -'', # 0xb6 -'', # 0xb7 -'', # 0xb8 -'', # 0xb9 -'', # 0xba -'', # 0xbb -'', # 0xbc -'', # 0xbd -'', # 0xbe -'', # 0xbf -'', # 0xc0 -'', # 0xc1 -'', # 0xc2 -'', # 0xc3 -'', # 0xc4 -'', # 0xc5 -'', # 0xc6 -'', # 0xc7 -'', # 0xc8 -'', # 0xc9 -'', # 0xca -'', # 0xcb -'', # 0xcc -'', # 0xcd -'', # 0xce -'', # 0xcf -'', # 0xd0 -'', # 0xd1 -'', # 0xd2 -'', # 0xd3 -'', # 0xd4 -'', # 0xd5 -'', # 0xd6 -'', # 0xd7 -'', # 0xd8 -'', # 0xd9 -'', # 0xda -'', # 0xdb -'', # 0xdc -'', # 0xdd -'', # 0xde -'', # 0xdf -'', # 0xe0 -'', # 0xe1 -'', # 0xe2 -'', # 0xe3 -'', # 0xe4 -'', # 0xe5 -'', # 0xe6 -'', # 0xe7 -'', # 0xe8 -'', # 0xe9 -'', # 0xea -'', # 0xeb -'', # 0xec -'', # 0xed -'', # 0xee -'', # 0xef -'', # 0xf0 -'', # 0xf1 -'', # 0xf2 -'', # 0xf3 -'', # 0xf4 -'', # 0xf5 -'', # 0xf6 -'', # 0xf7 -'', # 0xf8 -'', # 0xf9 -'', # 0xfa -'', # 0xfb -'', # 0xfc -'', # 0xfd -'', # 0xfe +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x02e.py b/libs/common/unidecode/x02e.py index feaad8d3..32a45fcd 100644 --- a/libs/common/unidecode/x02e.py +++ b/libs/common/unidecode/x02e.py @@ -1,257 +1,257 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f -'[?]', # 0x20 -'[?]', # 0x21 -'[?]', # 0x22 -'[?]', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 -'[?]', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?] ', # 0x80 -'[?] ', # 0x81 -'[?] ', # 0x82 -'[?] ', # 0x83 -'[?] ', # 0x84 -'[?] ', # 0x85 -'[?] ', # 0x86 -'[?] ', # 0x87 -'[?] ', # 0x88 -'[?] ', # 0x89 -'[?] ', # 0x8a -'[?] ', # 0x8b -'[?] ', # 0x8c -'[?] ', # 0x8d -'[?] ', # 0x8e -'[?] ', # 0x8f -'[?] ', # 0x90 -'[?] ', # 0x91 -'[?] ', # 0x92 -'[?] ', # 0x93 -'[?] ', # 0x94 -'[?] ', # 0x95 -'[?] ', # 0x96 -'[?] ', # 0x97 -'[?] ', # 0x98 -'[?] ', # 0x99 -'[?]', # 0x9a -'[?] ', # 0x9b -'[?] ', # 0x9c -'[?] ', # 0x9d -'[?] ', # 0x9e -'[?] ', # 0x9f -'[?] ', # 0xa0 -'[?] ', # 0xa1 -'[?] ', # 0xa2 -'[?] ', # 0xa3 -'[?] ', # 0xa4 -'[?] ', # 0xa5 -'[?] ', # 0xa6 -'[?] ', # 0xa7 -'[?] ', # 0xa8 -'[?] ', # 0xa9 -'[?] ', # 0xaa -'[?] ', # 0xab -'[?] ', # 0xac -'[?] ', # 0xad -'[?] ', # 0xae -'[?] ', # 0xaf -'[?] ', # 0xb0 -'[?] ', # 0xb1 -'[?] ', # 0xb2 -'[?] ', # 0xb3 -'[?] ', # 0xb4 -'[?] ', # 0xb5 -'[?] ', # 0xb6 -'[?] ', # 0xb7 -'[?] ', # 0xb8 -'[?] ', # 0xb9 -'[?] ', # 0xba -'[?] ', # 0xbb -'[?] ', # 0xbc -'[?] ', # 0xbd -'[?] ', # 0xbe -'[?] ', # 0xbf -'[?] ', # 0xc0 -'[?] ', # 0xc1 -'[?] ', # 0xc2 -'[?] ', # 0xc3 -'[?] ', # 0xc4 -'[?] ', # 0xc5 -'[?] ', # 0xc6 -'[?] ', # 0xc7 -'[?] ', # 0xc8 -'[?] ', # 0xc9 -'[?] ', # 0xca -'[?] ', # 0xcb -'[?] ', # 0xcc -'[?] ', # 0xcd -'[?] ', # 0xce -'[?] ', # 0xcf -'[?] ', # 0xd0 -'[?] ', # 0xd1 -'[?] ', # 0xd2 -'[?] ', # 0xd3 -'[?] ', # 0xd4 -'[?] ', # 0xd5 -'[?] ', # 0xd6 -'[?] ', # 0xd7 -'[?] ', # 0xd8 -'[?] ', # 0xd9 -'[?] ', # 0xda -'[?] ', # 0xdb -'[?] ', # 0xdc -'[?] ', # 0xdd -'[?] ', # 0xde -'[?] ', # 0xdf -'[?] ', # 0xe0 -'[?] ', # 0xe1 -'[?] ', # 0xe2 -'[?] ', # 0xe3 -'[?] ', # 0xe4 -'[?] ', # 0xe5 -'[?] ', # 0xe6 -'[?] ', # 0xe7 -'[?] ', # 0xe8 -'[?] ', # 0xe9 -'[?] ', # 0xea -'[?] ', # 0xeb -'[?] ', # 0xec -'[?] ', # 0xed -'[?] ', # 0xee -'[?] ', # 0xef -'[?] ', # 0xf0 -'[?] ', # 0xf1 -'[?] ', # 0xf2 -'[?] ', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +'r', # 0x00 +'r.', # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +'T', # 0x06 +'T.', # 0x07 +None, # 0x08 +'s', # 0x09 +None, # 0x0a +'[]', # 0x0b +'\\', # 0x0c +'/', # 0x0d +None, # 0x0e +'__', # 0x0f +None, # 0x10 +None, # 0x11 +'>', # 0x12 +'%', # 0x13 +None, # 0x14 +None, # 0x15 +'>', # 0x16 +'=', # 0x17 +None, # 0x18 +'/', # 0x19 +'-', # 0x1a +'~', # 0x1b +'\\', # 0x1c +'/', # 0x1d +'~', # 0x1e +'~', # 0x1f +'|-', # 0x20 +'-|', # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +'<=', # 0x26 +'=>', # 0x27 +'((', # 0x28 +'))', # 0x29 +None, # 0x2a +None, # 0x2b +'::', # 0x2c +None, # 0x2d +'?', # 0x2e +'\'', # 0x2f +'o', # 0x30 +'.', # 0x31 +',', # 0x32 +'.', # 0x33 +',', # 0x34 +';', # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +'----', # 0x3a +'------', # 0x3b +'x', # 0x3c +'|', # 0x3d +None, # 0x3e +None, # 0x3f +'=', # 0x40 +',', # 0x41 +'"', # 0x42 +'`--', # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x02f.py b/libs/common/unidecode/x02f.py index 01f8b15b..eccead0c 100644 --- a/libs/common/unidecode/x02f.py +++ b/libs/common/unidecode/x02f.py @@ -1,257 +1,257 @@ data = ( -'[?] ', # 0x00 -'[?] ', # 0x01 -'[?] ', # 0x02 -'[?] ', # 0x03 -'[?] ', # 0x04 -'[?] ', # 0x05 -'[?] ', # 0x06 -'[?] ', # 0x07 -'[?] ', # 0x08 -'[?] ', # 0x09 -'[?] ', # 0x0a -'[?] ', # 0x0b -'[?] ', # 0x0c -'[?] ', # 0x0d -'[?] ', # 0x0e -'[?] ', # 0x0f -'[?] ', # 0x10 -'[?] ', # 0x11 -'[?] ', # 0x12 -'[?] ', # 0x13 -'[?] ', # 0x14 -'[?] ', # 0x15 -'[?] ', # 0x16 -'[?] ', # 0x17 -'[?] ', # 0x18 -'[?] ', # 0x19 -'[?] ', # 0x1a -'[?] ', # 0x1b -'[?] ', # 0x1c -'[?] ', # 0x1d -'[?] ', # 0x1e -'[?] ', # 0x1f -'[?] ', # 0x20 -'[?] ', # 0x21 -'[?] ', # 0x22 -'[?] ', # 0x23 -'[?] ', # 0x24 -'[?] ', # 0x25 -'[?] ', # 0x26 -'[?] ', # 0x27 -'[?] ', # 0x28 -'[?] ', # 0x29 -'[?] ', # 0x2a -'[?] ', # 0x2b -'[?] ', # 0x2c -'[?] ', # 0x2d -'[?] ', # 0x2e -'[?] ', # 0x2f -'[?] ', # 0x30 -'[?] ', # 0x31 -'[?] ', # 0x32 -'[?] ', # 0x33 -'[?] ', # 0x34 -'[?] ', # 0x35 -'[?] ', # 0x36 -'[?] ', # 0x37 -'[?] ', # 0x38 -'[?] ', # 0x39 -'[?] ', # 0x3a -'[?] ', # 0x3b -'[?] ', # 0x3c -'[?] ', # 0x3d -'[?] ', # 0x3e -'[?] ', # 0x3f -'[?] ', # 0x40 -'[?] ', # 0x41 -'[?] ', # 0x42 -'[?] ', # 0x43 -'[?] ', # 0x44 -'[?] ', # 0x45 -'[?] ', # 0x46 -'[?] ', # 0x47 -'[?] ', # 0x48 -'[?] ', # 0x49 -'[?] ', # 0x4a -'[?] ', # 0x4b -'[?] ', # 0x4c -'[?] ', # 0x4d -'[?] ', # 0x4e -'[?] ', # 0x4f -'[?] ', # 0x50 -'[?] ', # 0x51 -'[?] ', # 0x52 -'[?] ', # 0x53 -'[?] ', # 0x54 -'[?] ', # 0x55 -'[?] ', # 0x56 -'[?] ', # 0x57 -'[?] ', # 0x58 -'[?] ', # 0x59 -'[?] ', # 0x5a -'[?] ', # 0x5b -'[?] ', # 0x5c -'[?] ', # 0x5d -'[?] ', # 0x5e -'[?] ', # 0x5f -'[?] ', # 0x60 -'[?] ', # 0x61 -'[?] ', # 0x62 -'[?] ', # 0x63 -'[?] ', # 0x64 -'[?] ', # 0x65 -'[?] ', # 0x66 -'[?] ', # 0x67 -'[?] ', # 0x68 -'[?] ', # 0x69 -'[?] ', # 0x6a -'[?] ', # 0x6b -'[?] ', # 0x6c -'[?] ', # 0x6d -'[?] ', # 0x6e -'[?] ', # 0x6f -'[?] ', # 0x70 -'[?] ', # 0x71 -'[?] ', # 0x72 -'[?] ', # 0x73 -'[?] ', # 0x74 -'[?] ', # 0x75 -'[?] ', # 0x76 -'[?] ', # 0x77 -'[?] ', # 0x78 -'[?] ', # 0x79 -'[?] ', # 0x7a -'[?] ', # 0x7b -'[?] ', # 0x7c -'[?] ', # 0x7d -'[?] ', # 0x7e -'[?] ', # 0x7f -'[?] ', # 0x80 -'[?] ', # 0x81 -'[?] ', # 0x82 -'[?] ', # 0x83 -'[?] ', # 0x84 -'[?] ', # 0x85 -'[?] ', # 0x86 -'[?] ', # 0x87 -'[?] ', # 0x88 -'[?] ', # 0x89 -'[?] ', # 0x8a -'[?] ', # 0x8b -'[?] ', # 0x8c -'[?] ', # 0x8d -'[?] ', # 0x8e -'[?] ', # 0x8f -'[?] ', # 0x90 -'[?] ', # 0x91 -'[?] ', # 0x92 -'[?] ', # 0x93 -'[?] ', # 0x94 -'[?] ', # 0x95 -'[?] ', # 0x96 -'[?] ', # 0x97 -'[?] ', # 0x98 -'[?] ', # 0x99 -'[?] ', # 0x9a -'[?] ', # 0x9b -'[?] ', # 0x9c -'[?] ', # 0x9d -'[?] ', # 0x9e -'[?] ', # 0x9f -'[?] ', # 0xa0 -'[?] ', # 0xa1 -'[?] ', # 0xa2 -'[?] ', # 0xa3 -'[?] ', # 0xa4 -'[?] ', # 0xa5 -'[?] ', # 0xa6 -'[?] ', # 0xa7 -'[?] ', # 0xa8 -'[?] ', # 0xa9 -'[?] ', # 0xaa -'[?] ', # 0xab -'[?] ', # 0xac -'[?] ', # 0xad -'[?] ', # 0xae -'[?] ', # 0xaf -'[?] ', # 0xb0 -'[?] ', # 0xb1 -'[?] ', # 0xb2 -'[?] ', # 0xb3 -'[?] ', # 0xb4 -'[?] ', # 0xb5 -'[?] ', # 0xb6 -'[?] ', # 0xb7 -'[?] ', # 0xb8 -'[?] ', # 0xb9 -'[?] ', # 0xba -'[?] ', # 0xbb -'[?] ', # 0xbc -'[?] ', # 0xbd -'[?] ', # 0xbe -'[?] ', # 0xbf -'[?] ', # 0xc0 -'[?] ', # 0xc1 -'[?] ', # 0xc2 -'[?] ', # 0xc3 -'[?] ', # 0xc4 -'[?] ', # 0xc5 -'[?] ', # 0xc6 -'[?] ', # 0xc7 -'[?] ', # 0xc8 -'[?] ', # 0xc9 -'[?] ', # 0xca -'[?] ', # 0xcb -'[?] ', # 0xcc -'[?] ', # 0xcd -'[?] ', # 0xce -'[?] ', # 0xcf -'[?] ', # 0xd0 -'[?] ', # 0xd1 -'[?] ', # 0xd2 -'[?] ', # 0xd3 -'[?] ', # 0xd4 -'[?] ', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?] ', # 0xf0 -'[?] ', # 0xf1 -'[?] ', # 0xf2 -'[?] ', # 0xf3 -'[?] ', # 0xf4 -'[?] ', # 0xf5 -'[?] ', # 0xf6 -'[?] ', # 0xf7 -'[?] ', # 0xf8 -'[?] ', # 0xf9 -'[?] ', # 0xfa -'[?] ', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x030.py b/libs/common/unidecode/x030.py index d65ed4c5..549ee824 100644 --- a/libs/common/unidecode/x030.py +++ b/libs/common/unidecode/x030.py @@ -58,12 +58,12 @@ data = ( '+10+', # 0x38 '+20+', # 0x39 '+30+', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d +None, # 0x3b +None, # 0x3c +None, # 0x3d '', # 0x3e '', # 0x3f -'[?]', # 0x40 +None, # 0x40 'a', # 0x41 'a', # 0x42 'i', # 0x43 @@ -148,18 +148,18 @@ data = ( 'wo', # 0x92 'n', # 0x93 'vu', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 '', # 0x99 '', # 0x9a '', # 0x9b '', # 0x9c '"', # 0x9d '"', # 0x9e -'[?]', # 0x9f -'[?]', # 0xa0 +None, # 0x9f +None, # 0xa0 'a', # 0xa1 'a', # 0xa2 'i', # 0xa3 diff --git a/libs/common/unidecode/x031.py b/libs/common/unidecode/x031.py index f5576080..fe57d2fb 100644 --- a/libs/common/unidecode/x031.py +++ b/libs/common/unidecode/x031.py @@ -1,9 +1,9 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 'B', # 0x05 'P', # 0x06 'M', # 0x07 @@ -44,10 +44,10 @@ data = ( 'V', # 0x2a 'NG', # 0x2b 'GN', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 'g', # 0x31 'gg', # 0x32 'gs', # 0x33 @@ -142,7 +142,7 @@ data = ( 'yu-i', # 0x8c 'U', # 0x8d 'U-i', # 0x8e -'[?]', # 0x8f +None, # 0x8f '', # 0x90 '', # 0x91 '', # 0x92 @@ -183,75 +183,75 @@ data = ( 'T', # 0xb5 'K', # 0xb6 'H', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x032.py b/libs/common/unidecode/x032.py index a0c21d11..2aa497a2 100644 --- a/libs/common/unidecode/x032.py +++ b/libs/common/unidecode/x032.py @@ -28,9 +28,9 @@ data = ( '(pa)', # 0x1a '(ha)', # 0x1b '(ju)', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f +None, # 0x1d +None, # 0x1e +None, # 0x1f '(1) ', # 0x20 '(2) ', # 0x21 '(3) ', # 0x22 @@ -67,19 +67,19 @@ data = ( '(Xiu) ', # 0x41 '<<', # 0x42 '>>', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 '21', # 0x51 '22', # 0x52 '23', # 0x53 @@ -123,9 +123,9 @@ data = ( '(ta)', # 0x79 '(pa)', # 0x7a '(ha)', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e +None, # 0x7c +None, # 0x7d +None, # 0x7e 'KIS ', # 0x7f '(1) ', # 0x80 '(2) ', # 0x81 diff --git a/libs/common/unidecode/x04d.py b/libs/common/unidecode/x04d.py index b025461a..eccead0c 100644 --- a/libs/common/unidecode/x04d.py +++ b/libs/common/unidecode/x04d.py @@ -1,257 +1,257 @@ data = ( -'[?] ', # 0x00 -'[?] ', # 0x01 -'[?] ', # 0x02 -'[?] ', # 0x03 -'[?] ', # 0x04 -'[?] ', # 0x05 -'[?] ', # 0x06 -'[?] ', # 0x07 -'[?] ', # 0x08 -'[?] ', # 0x09 -'[?] ', # 0x0a -'[?] ', # 0x0b -'[?] ', # 0x0c -'[?] ', # 0x0d -'[?] ', # 0x0e -'[?] ', # 0x0f -'[?] ', # 0x10 -'[?] ', # 0x11 -'[?] ', # 0x12 -'[?] ', # 0x13 -'[?] ', # 0x14 -'[?] ', # 0x15 -'[?] ', # 0x16 -'[?] ', # 0x17 -'[?] ', # 0x18 -'[?] ', # 0x19 -'[?] ', # 0x1a -'[?] ', # 0x1b -'[?] ', # 0x1c -'[?] ', # 0x1d -'[?] ', # 0x1e -'[?] ', # 0x1f -'[?] ', # 0x20 -'[?] ', # 0x21 -'[?] ', # 0x22 -'[?] ', # 0x23 -'[?] ', # 0x24 -'[?] ', # 0x25 -'[?] ', # 0x26 -'[?] ', # 0x27 -'[?] ', # 0x28 -'[?] ', # 0x29 -'[?] ', # 0x2a -'[?] ', # 0x2b -'[?] ', # 0x2c -'[?] ', # 0x2d -'[?] ', # 0x2e -'[?] ', # 0x2f -'[?] ', # 0x30 -'[?] ', # 0x31 -'[?] ', # 0x32 -'[?] ', # 0x33 -'[?] ', # 0x34 -'[?] ', # 0x35 -'[?] ', # 0x36 -'[?] ', # 0x37 -'[?] ', # 0x38 -'[?] ', # 0x39 -'[?] ', # 0x3a -'[?] ', # 0x3b -'[?] ', # 0x3c -'[?] ', # 0x3d -'[?] ', # 0x3e -'[?] ', # 0x3f -'[?] ', # 0x40 -'[?] ', # 0x41 -'[?] ', # 0x42 -'[?] ', # 0x43 -'[?] ', # 0x44 -'[?] ', # 0x45 -'[?] ', # 0x46 -'[?] ', # 0x47 -'[?] ', # 0x48 -'[?] ', # 0x49 -'[?] ', # 0x4a -'[?] ', # 0x4b -'[?] ', # 0x4c -'[?] ', # 0x4d -'[?] ', # 0x4e -'[?] ', # 0x4f -'[?] ', # 0x50 -'[?] ', # 0x51 -'[?] ', # 0x52 -'[?] ', # 0x53 -'[?] ', # 0x54 -'[?] ', # 0x55 -'[?] ', # 0x56 -'[?] ', # 0x57 -'[?] ', # 0x58 -'[?] ', # 0x59 -'[?] ', # 0x5a -'[?] ', # 0x5b -'[?] ', # 0x5c -'[?] ', # 0x5d -'[?] ', # 0x5e -'[?] ', # 0x5f -'[?] ', # 0x60 -'[?] ', # 0x61 -'[?] ', # 0x62 -'[?] ', # 0x63 -'[?] ', # 0x64 -'[?] ', # 0x65 -'[?] ', # 0x66 -'[?] ', # 0x67 -'[?] ', # 0x68 -'[?] ', # 0x69 -'[?] ', # 0x6a -'[?] ', # 0x6b -'[?] ', # 0x6c -'[?] ', # 0x6d -'[?] ', # 0x6e -'[?] ', # 0x6f -'[?] ', # 0x70 -'[?] ', # 0x71 -'[?] ', # 0x72 -'[?] ', # 0x73 -'[?] ', # 0x74 -'[?] ', # 0x75 -'[?] ', # 0x76 -'[?] ', # 0x77 -'[?] ', # 0x78 -'[?] ', # 0x79 -'[?] ', # 0x7a -'[?] ', # 0x7b -'[?] ', # 0x7c -'[?] ', # 0x7d -'[?] ', # 0x7e -'[?] ', # 0x7f -'[?] ', # 0x80 -'[?] ', # 0x81 -'[?] ', # 0x82 -'[?] ', # 0x83 -'[?] ', # 0x84 -'[?] ', # 0x85 -'[?] ', # 0x86 -'[?] ', # 0x87 -'[?] ', # 0x88 -'[?] ', # 0x89 -'[?] ', # 0x8a -'[?] ', # 0x8b -'[?] ', # 0x8c -'[?] ', # 0x8d -'[?] ', # 0x8e -'[?] ', # 0x8f -'[?] ', # 0x90 -'[?] ', # 0x91 -'[?] ', # 0x92 -'[?] ', # 0x93 -'[?] ', # 0x94 -'[?] ', # 0x95 -'[?] ', # 0x96 -'[?] ', # 0x97 -'[?] ', # 0x98 -'[?] ', # 0x99 -'[?] ', # 0x9a -'[?] ', # 0x9b -'[?] ', # 0x9c -'[?] ', # 0x9d -'[?] ', # 0x9e -'[?] ', # 0x9f -'[?] ', # 0xa0 -'[?] ', # 0xa1 -'[?] ', # 0xa2 -'[?] ', # 0xa3 -'[?] ', # 0xa4 -'[?] ', # 0xa5 -'[?] ', # 0xa6 -'[?] ', # 0xa7 -'[?] ', # 0xa8 -'[?] ', # 0xa9 -'[?] ', # 0xaa -'[?] ', # 0xab -'[?] ', # 0xac -'[?] ', # 0xad -'[?] ', # 0xae -'[?] ', # 0xaf -'[?] ', # 0xb0 -'[?] ', # 0xb1 -'[?] ', # 0xb2 -'[?] ', # 0xb3 -'[?] ', # 0xb4 -'[?] ', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x04e.py b/libs/common/unidecode/x04e.py index b472b855..6037a63e 100644 --- a/libs/common/unidecode/x04e.py +++ b/libs/common/unidecode/x04e.py @@ -5,7 +5,7 @@ data = ( 'Qi ', # 0x03 'Shang ', # 0x04 'Xia ', # 0x05 -'[?] ', # 0x06 +None, # 0x06 'Mo ', # 0x07 'Zhang ', # 0x08 'San ', # 0x09 @@ -73,7 +73,7 @@ data = ( 'Zhe ', # 0x47 'Yao ', # 0x48 'Yi ', # 0x49 -'[?] ', # 0x4a +None, # 0x4a 'Zhi ', # 0x4b 'Wu ', # 0x4c 'Zha ', # 0x4d @@ -90,7 +90,7 @@ data = ( 'Cheng ', # 0x58 'Yi ', # 0x59 'Yin ', # 0x5a -'[?] ', # 0x5b +None, # 0x5b 'Mie ', # 0x5c 'Jiu ', # 0x5d 'Qi ', # 0x5e @@ -100,7 +100,7 @@ data = ( 'Gai ', # 0x62 'Diu ', # 0x63 'Hal ', # 0x64 -'[?] ', # 0x65 +None, # 0x65 'Shu ', # 0x66 'Twul ', # 0x67 'Shi ', # 0x68 @@ -109,7 +109,7 @@ data = ( 'Jia ', # 0x6b 'Kel ', # 0x6c 'Shi ', # 0x6d -'[?] ', # 0x6e +None, # 0x6e 'Ol ', # 0x6f 'Mai ', # 0x70 'Luan ', # 0x71 @@ -124,7 +124,7 @@ data = ( 'Sol ', # 0x7a 'El ', # 0x7b 'Cwul ', # 0x7c -'[?] ', # 0x7d +None, # 0x7d 'Gan ', # 0x7e 'Chi ', # 0x7f 'Gui ', # 0x80 @@ -169,7 +169,7 @@ data = ( 'Chan ', # 0xa7 'Heng ', # 0xa8 'Mu ', # 0xa9 -'[?] ', # 0xaa +None, # 0xaa 'Xiang ', # 0xab 'Jing ', # 0xac 'Ting ', # 0xad @@ -232,7 +232,7 @@ data = ( 'Chao ', # 0xe6 'Chang ', # 0xe7 'Sa ', # 0xe8 -'[?] ', # 0xe9 +None, # 0xe9 'Yi ', # 0xea 'Mu ', # 0xeb 'Men ', # 0xec diff --git a/libs/common/unidecode/x04f.py b/libs/common/unidecode/x04f.py index 98c22910..3e372864 100644 --- a/libs/common/unidecode/x04f.py +++ b/libs/common/unidecode/x04f.py @@ -43,7 +43,7 @@ data = ( 'Xin ', # 0x29 'Wei ', # 0x2a 'Zhu ', # 0x2b -'[?] ', # 0x2c +None, # 0x2c 'Xuan ', # 0x2d 'Nu ', # 0x2e 'Bo ', # 0x2f @@ -101,9 +101,9 @@ data = ( 'Yong ', # 0x63 'Wa ', # 0x64 'Qian ', # 0x65 -'[?] ', # 0x66 +None, # 0x66 'Ka ', # 0x67 -'[?] ', # 0x68 +None, # 0x68 'Pei ', # 0x69 'Huai ', # 0x6a 'He ', # 0x6b @@ -230,12 +230,12 @@ data = ( 'Ti ', # 0xe4 'Che ', # 0xe5 'Chou ', # 0xe6 -'[?] ', # 0xe7 +None, # 0xe7 'Yan ', # 0xe8 'Lia ', # 0xe9 'Li ', # 0xea 'Lai ', # 0xeb -'[?] ', # 0xec +None, # 0xec 'Jian ', # 0xed 'Xiu ', # 0xee 'Fu ', # 0xef diff --git a/libs/common/unidecode/x050.py b/libs/common/unidecode/x050.py index 184b87fd..dbadd290 100644 --- a/libs/common/unidecode/x050.py +++ b/libs/common/unidecode/x050.py @@ -62,7 +62,7 @@ data = ( 'Zhi ', # 0x3c 'Sha ', # 0x3d 'Qing ', # 0x3e -'[?] ', # 0x3f +None, # 0x3f 'Ying ', # 0x40 'Cheng ', # 0x41 'Jian ', # 0x42 @@ -165,7 +165,7 @@ data = ( 'Dai ', # 0xa3 'Zai ', # 0xa4 'Tang ', # 0xa5 -'[?] ', # 0xa6 +None, # 0xa6 'Bin ', # 0xa7 'Chu ', # 0xa8 'Nuo ', # 0xa9 @@ -241,7 +241,7 @@ data = ( 'Lin ', # 0xef 'Bo ', # 0xf0 'Gu ', # 0xf1 -'[?] ', # 0xf2 +None, # 0xf2 'Su ', # 0xf3 'Xian ', # 0xf4 'Jiang ', # 0xf5 diff --git a/libs/common/unidecode/x051.py b/libs/common/unidecode/x051.py index c1928354..8dae426c 100644 --- a/libs/common/unidecode/x051.py +++ b/libs/common/unidecode/x051.py @@ -14,7 +14,7 @@ data = ( 'Jiao ', # 0x0c 'Sha ', # 0x0d 'Zai ', # 0x0e -'[?] ', # 0x0f +None, # 0x0f 'Bin ', # 0x10 'An ', # 0x11 'Ru ', # 0x12 @@ -110,7 +110,7 @@ data = ( 'Gong ', # 0x6c 'Liu ', # 0x6d 'Xi ', # 0x6e -'[?] ', # 0x6f +None, # 0x6f 'Lan ', # 0x70 'Gong ', # 0x71 'Tian ', # 0x72 diff --git a/libs/common/unidecode/x053.py b/libs/common/unidecode/x053.py index fa08b6ef..ab313b3c 100644 --- a/libs/common/unidecode/x053.py +++ b/libs/common/unidecode/x053.py @@ -6,7 +6,7 @@ data = ( 'Gai ', # 0x04 'Bao ', # 0x05 'Cong ', # 0x06 -'[?] ', # 0x07 +None, # 0x07 'Xiong ', # 0x08 'Peng ', # 0x09 'Ju ', # 0x0a @@ -128,7 +128,7 @@ data = ( 'E ', # 0x7e 'Qing ', # 0x7f 'Xi ', # 0x80 -'[?] ', # 0x81 +None, # 0x81 'Han ', # 0x82 'Zhan ', # 0x83 'E ', # 0x84 @@ -144,7 +144,7 @@ data = ( 'Zhi ', # 0x8e 'Zha ', # 0x8f 'Pang ', # 0x90 -'[?] ', # 0x91 +None, # 0x91 'He ', # 0x92 'Ya ', # 0x93 'Zhi ', # 0x94 @@ -253,6 +253,6 @@ data = ( 'Le ', # 0xfb 'Diao ', # 0xfc 'Ji ', # 0xfd -'[?] ', # 0xfe +None, # 0xfe 'Hong ', # 0xff ) diff --git a/libs/common/unidecode/x054.py b/libs/common/unidecode/x054.py index c014e0fe..b12b6242 100644 --- a/libs/common/unidecode/x054.py +++ b/libs/common/unidecode/x054.py @@ -89,7 +89,7 @@ data = ( 'Bai ', # 0x57 'Yuan ', # 0x58 'Kuai ', # 0x59 -'[?] ', # 0x5a +None, # 0x5a 'Qiang ', # 0x5b 'Wu ', # 0x5c 'E ', # 0x5d @@ -213,12 +213,12 @@ data = ( 'Xiao ', # 0xd3 'Bi ', # 0xd4 'Yue ', # 0xd5 -'[?] ', # 0xd6 +None, # 0xd6 'Hua ', # 0xd7 'Sasou ', # 0xd8 'Kuai ', # 0xd9 'Duo ', # 0xda -'[?] ', # 0xdb +None, # 0xdb 'Ji ', # 0xdc 'Nong ', # 0xdd 'Mou ', # 0xde diff --git a/libs/common/unidecode/x055.py b/libs/common/unidecode/x055.py index 26aea747..71be2089 100644 --- a/libs/common/unidecode/x055.py +++ b/libs/common/unidecode/x055.py @@ -120,7 +120,7 @@ data = ( 'Ding ', # 0x76 'Lang ', # 0x77 'Xiao ', # 0x78 -'[?] ', # 0x79 +None, # 0x79 'Tang ', # 0x7a 'Chi ', # 0x7b 'Ti ', # 0x7c @@ -243,7 +243,7 @@ data = ( 'Na ', # 0xf1 'Dia ', # 0xf2 'Ai ', # 0xf3 -'[?] ', # 0xf4 +None, # 0xf4 'Tong ', # 0xf5 'Bi ', # 0xf6 'Ao ', # 0xf7 diff --git a/libs/common/unidecode/x056.py b/libs/common/unidecode/x056.py index 30b7fa54..341e046f 100644 --- a/libs/common/unidecode/x056.py +++ b/libs/common/unidecode/x056.py @@ -144,8 +144,8 @@ data = ( 'Hao ', # 0x8e 'Ti ', # 0x8f 'Chang ', # 0x90 -'[?] ', # 0x91 -'[?] ', # 0x92 +None, # 0x91 +None, # 0x92 'Ca ', # 0x93 'Ti ', # 0x94 'Lu ', # 0x95 @@ -212,8 +212,8 @@ data = ( 'Lan ', # 0xd2 'Nie ', # 0xd3 'Nang ', # 0xd4 -'[?] ', # 0xd5 -'[?] ', # 0xd6 +None, # 0xd5 +None, # 0xd6 'Wei ', # 0xd7 'Hui ', # 0xd8 'Yin ', # 0xd9 diff --git a/libs/common/unidecode/x057.py b/libs/common/unidecode/x057.py index 9392fb86..fd6a5bfa 100644 --- a/libs/common/unidecode/x057.py +++ b/libs/common/unidecode/x057.py @@ -137,7 +137,7 @@ data = ( 'Ao ', # 0x87 'Tay ', # 0x88 'Pao ', # 0x89 -'[?] ', # 0x8a +None, # 0x8a 'Xing ', # 0x8b 'Dong ', # 0x8c 'Ji ', # 0x8d @@ -174,7 +174,7 @@ data = ( 'Hong ', # 0xac 'Wu ', # 0xad 'Kua ', # 0xae -'[?] ', # 0xaf +None, # 0xaf 'Tao ', # 0xb0 'Dang ', # 0xb1 'Kai ', # 0xb2 diff --git a/libs/common/unidecode/x058.py b/libs/common/unidecode/x058.py index 88057182..f23c1a6e 100644 --- a/libs/common/unidecode/x058.py +++ b/libs/common/unidecode/x058.py @@ -14,12 +14,12 @@ data = ( 'Gu ', # 0x0c 'Tu ', # 0x0d 'Leng ', # 0x0e -'[?] ', # 0x0f +None, # 0x0f 'Ya ', # 0x10 'Qian ', # 0x11 -'[?] ', # 0x12 +None, # 0x12 'An ', # 0x13 -'[?] ', # 0x14 +None, # 0x14 'Duo ', # 0x15 'Nao ', # 0x16 'Tu ', # 0x17 @@ -69,7 +69,7 @@ data = ( 'Huang ', # 0x43 'Leng ', # 0x44 'Duan ', # 0x45 -'[?] ', # 0x46 +None, # 0x46 'Xuan ', # 0x47 'Ji ', # 0x48 'Ji ', # 0x49 @@ -154,7 +154,7 @@ data = ( 'Qi ', # 0x98 'Qiang ', # 0x99 'Liang ', # 0x9a -'[?] ', # 0x9b +None, # 0x9b 'Zhui ', # 0x9c 'Qiao ', # 0x9d 'Zeng ', # 0x9e @@ -233,10 +233,10 @@ data = ( 'Yan ', # 0xe7 'Lei ', # 0xe8 'Ba ', # 0xe9 -'[?] ', # 0xea +None, # 0xea 'Shi ', # 0xeb 'Ren ', # 0xec -'[?] ', # 0xed +None, # 0xed 'Zhuang ', # 0xee 'Zhuang ', # 0xef 'Sheng ', # 0xf0 diff --git a/libs/common/unidecode/x059.py b/libs/common/unidecode/x059.py index 45966661..f850b0f4 100644 --- a/libs/common/unidecode/x059.py +++ b/libs/common/unidecode/x059.py @@ -16,7 +16,7 @@ data = ( 'Zuo ', # 0x0e 'Xia ', # 0x0f 'Xiong ', # 0x10 -'[?] ', # 0x11 +None, # 0x11 'Nao ', # 0x12 'Xia ', # 0x13 'Kui ', # 0x14 @@ -76,7 +76,7 @@ data = ( 'Xie ', # 0x4a 'Fen ', # 0x4b 'Dian ', # 0x4c -'[?] ', # 0x4d +None, # 0x4d 'Kui ', # 0x4e 'Zou ', # 0x4f 'Huan ', # 0x50 diff --git a/libs/common/unidecode/x05a.py b/libs/common/unidecode/x05a.py index be56e652..8e938646 100644 --- a/libs/common/unidecode/x05a.py +++ b/libs/common/unidecode/x05a.py @@ -50,7 +50,7 @@ data = ( 'Si ', # 0x30 'Yu ', # 0x31 'Wa ', # 0x32 -'[?] ', # 0x33 +None, # 0x33 'Xian ', # 0x34 'Ju ', # 0x35 'Qu ', # 0x36 @@ -170,7 +170,7 @@ data = ( 'Jiu ', # 0xa8 'Hu ', # 0xa9 'Ao ', # 0xaa -'[?] ', # 0xab +None, # 0xab 'Bou ', # 0xac 'Xu ', # 0xad 'Tou ', # 0xae diff --git a/libs/common/unidecode/x05b.py b/libs/common/unidecode/x05b.py index 1b167b3e..3d2ec495 100644 --- a/libs/common/unidecode/x05b.py +++ b/libs/common/unidecode/x05b.py @@ -102,7 +102,7 @@ data = ( 'Gu ', # 0x64 'Nu ', # 0x65 'Xue ', # 0x66 -'[?] ', # 0x67 +None, # 0x67 'Zhuan ', # 0x68 'Hai ', # 0x69 'Luan ', # 0x6a diff --git a/libs/common/unidecode/x05c.py b/libs/common/unidecode/x05c.py index 62957e87..bcb2df6d 100644 --- a/libs/common/unidecode/x05c.py +++ b/libs/common/unidecode/x05c.py @@ -32,7 +32,7 @@ data = ( 'Liao ', # 0x1e 'Xian ', # 0x1f 'Xian ', # 0x20 -'[?] ', # 0x21 +None, # 0x21 'Wang ', # 0x22 'Wang ', # 0x23 'You ', # 0x24 @@ -86,7 +86,7 @@ data = ( 'Ni ', # 0x54 'Zhan ', # 0x55 'Xi ', # 0x56 -'[?] ', # 0x57 +None, # 0x57 'Man ', # 0x58 'E ', # 0x59 'Lou ', # 0x5a @@ -113,12 +113,12 @@ data = ( 'Tun ', # 0x6f 'Ni ', # 0x70 'Shan ', # 0x71 -'[?] ', # 0x72 +None, # 0x72 'Xian ', # 0x73 'Li ', # 0x74 'Xue ', # 0x75 'Nata ', # 0x76 -'[?] ', # 0x77 +None, # 0x77 'Long ', # 0x78 'Yi ', # 0x79 'Qi ', # 0x7a @@ -130,7 +130,7 @@ data = ( 'Chu ', # 0x80 'Sui ', # 0x81 'Qi ', # 0x82 -'[?] ', # 0x83 +None, # 0x83 'Yue ', # 0x84 'Ban ', # 0x85 'Yao ', # 0x86 diff --git a/libs/common/unidecode/x05d.py b/libs/common/unidecode/x05d.py index c85032aa..6f43044c 100644 --- a/libs/common/unidecode/x05d.py +++ b/libs/common/unidecode/x05d.py @@ -47,7 +47,7 @@ data = ( 'Zhan ', # 0x2d 'Gu ', # 0x2e 'Yin ', # 0x2f -'[?] ', # 0x30 +None, # 0x30 'Ze ', # 0x31 'Huang ', # 0x32 'Yu ', # 0x33 @@ -116,7 +116,7 @@ data = ( 'Nie ', # 0x72 'Cuo ', # 0x73 'Ji ', # 0x74 -'[?] ', # 0x75 +None, # 0x75 'Tao ', # 0x76 'Song ', # 0x77 'Zong ', # 0x78 @@ -181,7 +181,7 @@ data = ( 'Di ', # 0xb3 'Ao ', # 0xb4 'Zui ', # 0xb5 -'[?] ', # 0xb6 +None, # 0xb6 'Ni ', # 0xb7 'Rong ', # 0xb8 'Dao ', # 0xb9 @@ -190,7 +190,7 @@ data = ( 'Yu ', # 0xbc 'Yue ', # 0xbd 'Yin ', # 0xbe -'[?] ', # 0xbf +None, # 0xbf 'Jie ', # 0xc0 'Li ', # 0xc1 'Sui ', # 0xc2 @@ -212,7 +212,7 @@ data = ( 'Luan ', # 0xd2 'Dian ', # 0xd3 'Dian ', # 0xd4 -'[?] ', # 0xd5 +None, # 0xd5 'Yan ', # 0xd6 'Yan ', # 0xd7 'Yan ', # 0xd8 diff --git a/libs/common/unidecode/x05e.py b/libs/common/unidecode/x05e.py index af879280..4f587404 100644 --- a/libs/common/unidecode/x05e.py +++ b/libs/common/unidecode/x05e.py @@ -100,7 +100,7 @@ data = ( 'Chuang ', # 0x62 'Bi ', # 0x63 'Hei ', # 0x64 -'[?] ', # 0x65 +None, # 0x65 'Mi ', # 0x66 'Qiao ', # 0x67 'Chan ', # 0x68 @@ -145,7 +145,7 @@ data = ( 'Xu ', # 0x8f 'Lu ', # 0x90 'Wu ', # 0x91 -'[?] ', # 0x92 +None, # 0x92 'Ku ', # 0x93 'Ying ', # 0x94 'Di ', # 0x95 @@ -236,7 +236,7 @@ data = ( 'Lin ', # 0xea 'Liao ', # 0xeb 'Lu ', # 0xec -'[?] ', # 0xed +None, # 0xed 'Ying ', # 0xee 'Xian ', # 0xef 'Ting ', # 0xf0 diff --git a/libs/common/unidecode/x05f.py b/libs/common/unidecode/x05f.py index 032eab89..547d75db 100644 --- a/libs/common/unidecode/x05f.py +++ b/libs/common/unidecode/x05f.py @@ -147,13 +147,13 @@ data = ( 'Jing ', # 0x91 'Tu ', # 0x92 'Cong ', # 0x93 -'[?] ', # 0x94 +None, # 0x94 'Lai ', # 0x95 'Cong ', # 0x96 'De ', # 0x97 'Pai ', # 0x98 'Xi ', # 0x99 -'[?] ', # 0x9a +None, # 0x9a 'Qi ', # 0x9b 'Chang ', # 0x9c 'Zhi ', # 0x9d diff --git a/libs/common/unidecode/x060.py b/libs/common/unidecode/x060.py index ad3728f8..5487c560 100644 --- a/libs/common/unidecode/x060.py +++ b/libs/common/unidecode/x060.py @@ -60,7 +60,7 @@ data = ( 'Koraeru ', # 0x3a 'Zong ', # 0x3b 'Dui ', # 0x3c -'[?] ', # 0x3d +None, # 0x3d 'Ki ', # 0x3e 'Yi ', # 0x3f 'Chi ', # 0x40 diff --git a/libs/common/unidecode/x061.py b/libs/common/unidecode/x061.py index 6e8ab80d..39a3b4bb 100644 --- a/libs/common/unidecode/x061.py +++ b/libs/common/unidecode/x061.py @@ -36,7 +36,7 @@ data = ( 'Sai ', # 0x22 'Leng ', # 0x23 'Fen ', # 0x24 -'[?] ', # 0x25 +None, # 0x25 'Kui ', # 0x26 'Kui ', # 0x27 'Que ', # 0x28 @@ -79,7 +79,7 @@ data = ( 'Yun ', # 0x4d 'Shen ', # 0x4e 'Ming ', # 0x4f -'[?] ', # 0x50 +None, # 0x50 'She ', # 0x51 'Cong ', # 0x52 'Piao ', # 0x53 @@ -242,7 +242,7 @@ data = ( 'Liu ', # 0xf0 'Mie ', # 0xf1 'Cheng ', # 0xf2 -'[?] ', # 0xf3 +None, # 0xf3 'Chan ', # 0xf4 'Meng ', # 0xf5 'Lan ', # 0xf6 diff --git a/libs/common/unidecode/x062.py b/libs/common/unidecode/x062.py index 97979203..ffa55aef 100644 --- a/libs/common/unidecode/x062.py +++ b/libs/common/unidecode/x062.py @@ -162,7 +162,7 @@ data = ( 'Kou ', # 0xa0 'Lun ', # 0xa1 'Qiang ', # 0xa2 -'[?] ', # 0xa3 +None, # 0xa3 'Hu ', # 0xa4 'Bao ', # 0xa5 'Bing ', # 0xa6 @@ -227,7 +227,7 @@ data = ( 'Kuo ', # 0xe1 'Long ', # 0xe2 'Jian ', # 0xe3 -'[?] ', # 0xe4 +None, # 0xe4 'Yong ', # 0xe5 'Lan ', # 0xe6 'Ning ', # 0xe7 diff --git a/libs/common/unidecode/x063.py b/libs/common/unidecode/x063.py index 896cea25..3906e52a 100644 --- a/libs/common/unidecode/x063.py +++ b/libs/common/unidecode/x063.py @@ -99,7 +99,7 @@ data = ( 'Jian ', # 0x61 'Huan ', # 0x62 'Dao ', # 0x63 -'[?] ', # 0x64 +None, # 0x64 'Wan ', # 0x65 'Qin ', # 0x66 'Peng ', # 0x67 @@ -181,7 +181,7 @@ data = ( 'Lu ', # 0xb3 'Guo ', # 0xb4 'Haba ', # 0xb5 -'[?] ', # 0xb6 +None, # 0xb6 'Zhi ', # 0xb7 'Dan ', # 0xb8 'Mang ', # 0xb9 @@ -250,8 +250,8 @@ data = ( 'Zha ', # 0xf8 'Bei ', # 0xf9 'Yao ', # 0xfa -'[?] ', # 0xfb -'[?] ', # 0xfc +None, # 0xfb +None, # 0xfc 'Lan ', # 0xfd 'Wen ', # 0xfe 'Qin ', # 0xff diff --git a/libs/common/unidecode/x064.py b/libs/common/unidecode/x064.py index dc1514b6..d36ffd89 100644 --- a/libs/common/unidecode/x064.py +++ b/libs/common/unidecode/x064.py @@ -181,7 +181,7 @@ data = ( 'Qin ', # 0xb3 'Dun ', # 0xb4 'Nian ', # 0xb5 -'[?] ', # 0xb6 +None, # 0xb6 'Xie ', # 0xb7 'Lu ', # 0xb8 'Jiao ', # 0xb9 @@ -219,7 +219,7 @@ data = ( 'Ao ', # 0xd9 'Ju ', # 0xda 'Ye ', # 0xdb -'[?] ', # 0xdc +None, # 0xdc 'Mang ', # 0xdd 'Sou ', # 0xde 'Mi ', # 0xdf diff --git a/libs/common/unidecode/x065.py b/libs/common/unidecode/x065.py index ede51764..5390e96e 100644 --- a/libs/common/unidecode/x065.py +++ b/libs/common/unidecode/x065.py @@ -25,7 +25,7 @@ data = ( 'Mei ', # 0x17 'Rang ', # 0x18 'Chan ', # 0x19 -'[?] ', # 0x1a +None, # 0x1a 'Cuan ', # 0x1b 'Xi ', # 0x1c 'She ', # 0x1d @@ -142,7 +142,7 @@ data = ( 'Bin ', # 0x8c 'Jue ', # 0x8d 'Zhai ', # 0x8e -'[?] ', # 0x8f +None, # 0x8f 'Fei ', # 0x90 'Ban ', # 0x91 'Ban ', # 0x92 diff --git a/libs/common/unidecode/x066.py b/libs/common/unidecode/x066.py index 01898d55..08549f49 100644 --- a/libs/common/unidecode/x066.py +++ b/libs/common/unidecode/x066.py @@ -225,7 +225,7 @@ data = ( 'Chen ', # 0xdf 'Kuang ', # 0xe0 'Die ', # 0xe1 -'[?] ', # 0xe2 +None, # 0xe2 'Yan ', # 0xe3 'Huo ', # 0xe4 'Lu ', # 0xe5 diff --git a/libs/common/unidecode/x067.py b/libs/common/unidecode/x067.py index 2e863ae0..51cab591 100644 --- a/libs/common/unidecode/x067.py +++ b/libs/common/unidecode/x067.py @@ -16,7 +16,7 @@ data = ( 'Ling ', # 0x0e 'Fei ', # 0x0f 'Qu ', # 0x10 -'[?] ', # 0x11 +None, # 0x11 'Nu ', # 0x12 'Tiao ', # 0x13 'Shuo ', # 0x14 @@ -36,7 +36,7 @@ data = ( 'Wang ', # 0x22 'Tong ', # 0x23 'Lang ', # 0x24 -'[?] ', # 0x25 +None, # 0x25 'Meng ', # 0x26 'Long ', # 0x27 'Mu ', # 0x28 @@ -47,7 +47,7 @@ data = ( 'Zha ', # 0x2d 'Zhu ', # 0x2e 'Zhu ', # 0x2f -'[?] ', # 0x30 +None, # 0x30 'Zhu ', # 0x31 'Ren ', # 0x32 'Ba ', # 0x33 @@ -163,7 +163,7 @@ data = ( 'Dou ', # 0xa1 'Shu ', # 0xa2 'Zao ', # 0xa3 -'[?] ', # 0xa4 +None, # 0xa4 'Li ', # 0xa5 'Haze ', # 0xa6 'Jian ', # 0xa7 diff --git a/libs/common/unidecode/x068.py b/libs/common/unidecode/x068.py index c562311c..822ec5f9 100644 --- a/libs/common/unidecode/x068.py +++ b/libs/common/unidecode/x068.py @@ -5,7 +5,7 @@ data = ( 'Hoy ', # 0x03 'Rong ', # 0x04 'Zha ', # 0x05 -'[?] ', # 0x06 +None, # 0x06 'Biao ', # 0x07 'Zhan ', # 0x08 'Jie ', # 0x09 @@ -93,7 +93,7 @@ data = ( 'Kasei ', # 0x5b 'Ying ', # 0x5c 'Masu ', # 0x5d -'[?] ', # 0x5e +None, # 0x5e 'Zhan ', # 0x5f 'Ya ', # 0x60 'Nao ', # 0x61 diff --git a/libs/common/unidecode/x069.py b/libs/common/unidecode/x069.py index 7fa8c7de..ed13a6ad 100644 --- a/libs/common/unidecode/x069.py +++ b/libs/common/unidecode/x069.py @@ -40,10 +40,10 @@ data = ( 'Ken ', # 0x26 'Myeng ', # 0x27 'Tafu ', # 0x28 -'[?] ', # 0x29 +None, # 0x29 'Peng ', # 0x2a 'Zhan ', # 0x2b -'[?] ', # 0x2c +None, # 0x2c 'Tuo ', # 0x2d 'Sen ', # 0x2e 'Duo ', # 0x2f @@ -138,7 +138,7 @@ data = ( 'Lu ', # 0x88 'Ju ', # 0x89 'Sakaki ', # 0x8a -'[?] ', # 0x8b +None, # 0x8b 'Pi ', # 0x8c 'Xie ', # 0x8d 'Jia ', # 0x8e @@ -224,7 +224,7 @@ data = ( 'Ori ', # 0xde 'Bin ', # 0xdf 'Zhu ', # 0xe0 -'[?] ', # 0xe1 +None, # 0xe1 'Xi ', # 0xe2 'Qi ', # 0xe3 'Lian ', # 0xe4 diff --git a/libs/common/unidecode/x06a.py b/libs/common/unidecode/x06a.py index 12fcabd5..5f5abdf5 100644 --- a/libs/common/unidecode/x06a.py +++ b/libs/common/unidecode/x06a.py @@ -44,7 +44,7 @@ data = ( 'Heng ', # 0x2a 'Jian ', # 0x2b 'Cong ', # 0x2c -'[?] ', # 0x2d +None, # 0x2d 'Hokuso ', # 0x2e 'Qiang ', # 0x2f 'Tara ', # 0x30 @@ -121,8 +121,8 @@ data = ( 'Dou ', # 0x77 'Shou ', # 0x78 'Lu ', # 0x79 -'[?] ', # 0x7a -'[?] ', # 0x7b +None, # 0x7a +None, # 0x7b 'Yuan ', # 0x7c 'Ta ', # 0x7d 'Shu ', # 0x7e @@ -201,7 +201,7 @@ data = ( 'Po ', # 0xc7 'Deng ', # 0xc8 'Chu ', # 0xc9 -'[?] ', # 0xca +None, # 0xca 'Mian ', # 0xcb 'You ', # 0xcc 'Zhi ', # 0xcd @@ -229,7 +229,7 @@ data = ( 'Lian ', # 0xe3 'Tamo ', # 0xe4 'Chu ', # 0xe5 -'[?] ', # 0xe6 +None, # 0xe6 'Zhu ', # 0xe7 'Lu ', # 0xe8 'Yan ', # 0xe9 @@ -244,7 +244,7 @@ data = ( 'Yu ', # 0xf2 'Long ', # 0xf3 'Lai ', # 0xf4 -'[?] ', # 0xf5 +None, # 0xf5 'Xian ', # 0xf6 'Kwi ', # 0xf7 'Ju ', # 0xf8 diff --git a/libs/common/unidecode/x06b.py b/libs/common/unidecode/x06b.py index 56aa7c65..417056c5 100644 --- a/libs/common/unidecode/x06b.py +++ b/libs/common/unidecode/x06b.py @@ -12,7 +12,7 @@ data = ( 'Quan ', # 0x0a 'Qu ', # 0x0b 'Cang ', # 0x0c -'[?] ', # 0x0d +None, # 0x0d 'Yu ', # 0x0e 'Luo ', # 0x0f 'Li ', # 0x10 @@ -219,8 +219,8 @@ data = ( 'Bi ', # 0xd9 'Chan ', # 0xda 'Mao ', # 0xdb -'[?] ', # 0xdc -'[?] ', # 0xdd +None, # 0xdc +None, # 0xdd 'Pu ', # 0xde 'Mushiru ', # 0xdf 'Jia ', # 0xe0 @@ -245,7 +245,7 @@ data = ( 'Cui ', # 0xf3 'Bi ', # 0xf4 'San ', # 0xf5 -'[?] ', # 0xf6 +None, # 0xf6 'Mao ', # 0xf7 'Sui ', # 0xf8 'Yu ', # 0xf9 diff --git a/libs/common/unidecode/x06c.py b/libs/common/unidecode/x06c.py index a1534e74..db7650ab 100644 --- a/libs/common/unidecode/x06c.py +++ b/libs/common/unidecode/x06c.py @@ -29,7 +29,7 @@ data = ( 'Fen ', # 0x1b 'Ri ', # 0x1c 'Nei ', # 0x1d -'[?] ', # 0x1e +None, # 0x1e 'Fu ', # 0x1f 'Shen ', # 0x20 'Dong ', # 0x21 @@ -98,7 +98,7 @@ data = ( 'Chi ', # 0x60 'Wu ', # 0x61 'Tsuchi ', # 0x62 -'[?] ', # 0x63 +None, # 0x63 'Tang ', # 0x64 'Zhi ', # 0x65 'Chi ', # 0x66 @@ -248,7 +248,7 @@ data = ( 'Xue ', # 0xf6 'Long ', # 0xf7 'Lu ', # 0xf8 -'[?] ', # 0xf9 +None, # 0xf9 'Bo ', # 0xfa 'Xie ', # 0xfb 'Po ', # 0xfc diff --git a/libs/common/unidecode/x06d.py b/libs/common/unidecode/x06d.py index a9113465..da29aabc 100644 --- a/libs/common/unidecode/x06d.py +++ b/libs/common/unidecode/x06d.py @@ -27,7 +27,7 @@ data = ( 'Zhu ', # 0x19 'Jiang ', # 0x1a 'Luo ', # 0x1b -'[?] ', # 0x1c +None, # 0x1c 'An ', # 0x1d 'Dong ', # 0x1e 'Yi ', # 0x1f @@ -164,7 +164,7 @@ data = ( 'Yun ', # 0xa2 'Huan ', # 0xa3 'Di ', # 0xa4 -'[?] ', # 0xa5 +None, # 0xa5 'Run ', # 0xa6 'Jian ', # 0xa7 'Zhang ', # 0xa8 diff --git a/libs/common/unidecode/x06e.py b/libs/common/unidecode/x06e.py index d4698fd4..e12e6598 100644 --- a/libs/common/unidecode/x06e.py +++ b/libs/common/unidecode/x06e.py @@ -14,7 +14,7 @@ data = ( 'Lu ', # 0x0c 'Zi ', # 0x0d 'Du ', # 0x0e -'[?] ', # 0x0f +None, # 0x0f 'Jian ', # 0x10 'Min ', # 0x11 'Pi ', # 0x12 @@ -131,14 +131,14 @@ data = ( 'Ying ', # 0x81 'Ratsu ', # 0x82 'Kui ', # 0x83 -'[?] ', # 0x84 +None, # 0x84 'Jian ', # 0x85 'Xu ', # 0x86 'Lu ', # 0x87 'Gui ', # 0x88 'Gai ', # 0x89 -'[?] ', # 0x8a -'[?] ', # 0x8b +None, # 0x8a +None, # 0x8b 'Po ', # 0x8c 'Jin ', # 0x8d 'Gui ', # 0x8e @@ -230,7 +230,7 @@ data = ( 'Lu ', # 0xe4 'Lan ', # 0xe5 'Luan ', # 0xe6 -'[?] ', # 0xe7 +None, # 0xe7 'Bin ', # 0xe8 'Tan ', # 0xe9 'Yu ', # 0xea diff --git a/libs/common/unidecode/x06f.py b/libs/common/unidecode/x06f.py index 36bf2a26..822a628a 100644 --- a/libs/common/unidecode/x06f.py +++ b/libs/common/unidecode/x06f.py @@ -71,8 +71,8 @@ data = ( 'Guan ', # 0x45 'Ying ', # 0x46 'Xiao ', # 0x47 -'[?] ', # 0x48 -'[?] ', # 0x49 +None, # 0x48 +None, # 0x49 'Xu ', # 0x4a 'Lian ', # 0x4b 'Zhi ', # 0x4c @@ -154,9 +154,9 @@ data = ( 'Shan ', # 0x98 'Xi ', # 0x99 'Oki ', # 0x9a -'[?] ', # 0x9b +None, # 0x9b 'Lan ', # 0x9c -'[?] ', # 0x9d +None, # 0x9d 'Yu ', # 0x9e 'Lin ', # 0x9f 'Min ', # 0xa0 @@ -206,7 +206,7 @@ data = ( 'Ta ', # 0xcc 'Song ', # 0xcd 'Ding ', # 0xce -'[?] ', # 0xcf +None, # 0xcf 'Zhu ', # 0xd0 'Lai ', # 0xd1 'Bin ', # 0xd2 @@ -247,7 +247,7 @@ data = ( 'Hama ', # 0xf5 'Kuo ', # 0xf6 'Fei ', # 0xf7 -'[?] ', # 0xf8 +None, # 0xf8 'Boku ', # 0xf9 'Jian ', # 0xfa 'Wei ', # 0xfb diff --git a/libs/common/unidecode/x070.py b/libs/common/unidecode/x070.py index b12567ff..4330f273 100644 --- a/libs/common/unidecode/x070.py +++ b/libs/common/unidecode/x070.py @@ -44,8 +44,8 @@ data = ( 'Fan ', # 0x2a 'Hu ', # 0x2b 'Lai ', # 0x2c -'[?] ', # 0x2d -'[?] ', # 0x2e +None, # 0x2d +None, # 0x2e 'Ying ', # 0x2f 'Mi ', # 0x30 'Ji ', # 0x31 @@ -91,7 +91,7 @@ data = ( 'Dang ', # 0x59 'Jiao ', # 0x5a 'Chan ', # 0x5b -'[?] ', # 0x5c +None, # 0x5c 'Hao ', # 0x5d 'Ba ', # 0x5e 'Zhu ', # 0x5f @@ -157,7 +157,7 @@ data = ( 'Guang ', # 0x9b 'Wei ', # 0x9c 'Qiang ', # 0x9d -'[?] ', # 0x9e +None, # 0x9e 'Da ', # 0x9f 'Xia ', # 0xa0 'Zheng ', # 0xa1 @@ -190,7 +190,7 @@ data = ( 'Lian ', # 0xbc 'Chi ', # 0xbd 'Huang ', # 0xbe -'[?] ', # 0xbf +None, # 0xbf 'Hu ', # 0xc0 'Shuo ', # 0xc1 'Lan ', # 0xc2 @@ -228,16 +228,16 @@ data = ( 'Zhe ', # 0xe2 'Hui ', # 0xe3 'Kao ', # 0xe4 -'[?] ', # 0xe5 +None, # 0xe5 'Fan ', # 0xe6 'Shao ', # 0xe7 'Ye ', # 0xe8 'Hui ', # 0xe9 -'[?] ', # 0xea +None, # 0xea 'Tang ', # 0xeb 'Jin ', # 0xec 'Re ', # 0xed -'[?] ', # 0xee +None, # 0xee 'Xi ', # 0xef 'Fu ', # 0xf0 'Jiong ', # 0xf1 diff --git a/libs/common/unidecode/x071.py b/libs/common/unidecode/x071.py index bad8f7ea..5d0b73f0 100644 --- a/libs/common/unidecode/x071.py +++ b/libs/common/unidecode/x071.py @@ -16,8 +16,8 @@ data = ( 'Xie ', # 0x0e 'Ji ', # 0x0f 'Wu ', # 0x10 -'[?] ', # 0x11 -'[?] ', # 0x12 +None, # 0x11 +None, # 0x12 'Han ', # 0x13 'Yan ', # 0x14 'Huan ', # 0x15 @@ -56,14 +56,14 @@ data = ( 'Ran ', # 0x36 'Pi ', # 0x37 'Gu ', # 0x38 -'[?] ', # 0x39 +None, # 0x39 'Sheng ', # 0x3a 'Chang ', # 0x3b 'Shao ', # 0x3c -'[?] ', # 0x3d -'[?] ', # 0x3e -'[?] ', # 0x3f -'[?] ', # 0x40 +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 'Chen ', # 0x41 'He ', # 0x42 'Kui ', # 0x43 @@ -117,8 +117,8 @@ data = ( 'Hu ', # 0x73 'Yun ', # 0x74 'Xia ', # 0x75 -'[?] ', # 0x76 -'[?] ', # 0x77 +None, # 0x76 +None, # 0x77 'Bian ', # 0x78 'Gou ', # 0x79 'Tui ', # 0x7a @@ -149,7 +149,7 @@ data = ( 'Wen ', # 0x93 'Rong ', # 0x94 'Oozutsu ', # 0x95 -'[?] ', # 0x96 +None, # 0x96 'Qiang ', # 0x97 'Liu ', # 0x98 'Xi ', # 0x99 @@ -179,7 +179,7 @@ data = ( 'Re ', # 0xb1 'Jiong ', # 0xb2 'Man ', # 0xb3 -'[?] ', # 0xb4 +None, # 0xb4 'Shang ', # 0xb5 'Cuan ', # 0xb6 'Zeng ', # 0xb7 @@ -220,8 +220,8 @@ data = ( 'Yi ', # 0xda 'Jing ', # 0xdb 'Men ', # 0xdc -'[?] ', # 0xdd -'[?] ', # 0xde +None, # 0xdd +None, # 0xde 'Ying ', # 0xdf 'Yu ', # 0xe0 'Yi ', # 0xe1 diff --git a/libs/common/unidecode/x072.py b/libs/common/unidecode/x072.py index c91c93cb..eb48d956 100644 --- a/libs/common/unidecode/x072.py +++ b/libs/common/unidecode/x072.py @@ -13,7 +13,7 @@ data = ( 'Xun ', # 0x0b 'Kuang ', # 0x0c 'Shuo ', # 0x0d -'[?] ', # 0x0e +None, # 0x0e 'Li ', # 0x0f 'Lu ', # 0x10 'Jue ', # 0x11 @@ -23,7 +23,7 @@ data = ( 'Xie ', # 0x15 'Long ', # 0x16 'Ye ', # 0x17 -'[?] ', # 0x18 +None, # 0x18 'Rang ', # 0x19 'Yue ', # 0x1a 'Lan ', # 0x1b @@ -31,13 +31,13 @@ data = ( 'Jue ', # 0x1d 'Tong ', # 0x1e 'Guan ', # 0x1f -'[?] ', # 0x20 +None, # 0x20 'Che ', # 0x21 'Mi ', # 0x22 'Tang ', # 0x23 'Lan ', # 0x24 'Zhu ', # 0x25 -'[?] ', # 0x26 +None, # 0x26 'Ling ', # 0x27 'Cuan ', # 0x28 'Yu ', # 0x29 @@ -50,7 +50,7 @@ data = ( 'Yuan ', # 0x30 'Ai ', # 0x31 'Wei ', # 0x32 -'[?] ', # 0x33 +None, # 0x33 'Jue ', # 0x34 'Jue ', # 0x35 'Fu ', # 0x36 @@ -86,7 +86,7 @@ data = ( 'Bo ', # 0x54 'Chuang ', # 0x55 'You ', # 0x56 -'[?] ', # 0x57 +None, # 0x57 'Du ', # 0x58 'Ya ', # 0x59 'Cheng ', # 0x5a @@ -157,7 +157,7 @@ data = ( 'Li ', # 0x9b 'Dun ', # 0x9c 'Tong ', # 0x9d -'[?] ', # 0x9e +None, # 0x9e 'Jiang ', # 0x9f 'Ikenie ', # 0xa0 'Li ', # 0xa1 diff --git a/libs/common/unidecode/x073.py b/libs/common/unidecode/x073.py index 4cf0176d..8e294413 100644 --- a/libs/common/unidecode/x073.py +++ b/libs/common/unidecode/x073.py @@ -70,7 +70,7 @@ data = ( 'Yu ', # 0x44 'Shi ', # 0x45 'Hao ', # 0x46 -'[?] ', # 0x47 +None, # 0x47 'Yi ', # 0x48 'Zhen ', # 0x49 'Chuang ', # 0x4a @@ -238,7 +238,7 @@ data = ( 'Xu ', # 0xec 'Ban ', # 0xed 'Pei ', # 0xee -'[?] ', # 0xef +None, # 0xef 'Dang ', # 0xf0 'Ei ', # 0xf1 'Hun ', # 0xf2 diff --git a/libs/common/unidecode/x074.py b/libs/common/unidecode/x074.py index 312fc646..c44a3a08 100644 --- a/libs/common/unidecode/x074.py +++ b/libs/common/unidecode/x074.py @@ -17,7 +17,7 @@ data = ( 'Lian ', # 0x0f 'Suo ', # 0x10 'Chiisai ', # 0x11 -'[?] ', # 0x12 +None, # 0x12 'Wan ', # 0x13 'Dian ', # 0x14 'Pin ', # 0x15 @@ -58,7 +58,7 @@ data = ( 'Zhuo ', # 0x38 'Qin ', # 0x39 'Fa ', # 0x3a -'[?] ', # 0x3b +None, # 0x3b 'Qiong ', # 0x3c 'Du ', # 0x3d 'Jie ', # 0x3e @@ -140,7 +140,7 @@ data = ( 'Man ', # 0x8a 'Zhang ', # 0x8b 'Yin ', # 0x8c -'[?] ', # 0x8d +None, # 0x8d 'Ying ', # 0x8e 'Zhi ', # 0x8f 'Lu ', # 0x90 @@ -163,7 +163,7 @@ data = ( 'Jin ', # 0xa1 'Liu ', # 0xa2 'Ji ', # 0xa3 -'[?] ', # 0xa4 +None, # 0xa4 'Jing ', # 0xa5 'Ai ', # 0xa6 'Bi ', # 0xa7 @@ -179,7 +179,7 @@ data = ( 'Se ', # 0xb1 'Sui ', # 0xb2 'Tian ', # 0xb3 -'[?] ', # 0xb4 +None, # 0xb4 'Yu ', # 0xb5 'Jin ', # 0xb6 'Lu ', # 0xb7 diff --git a/libs/common/unidecode/x076.py b/libs/common/unidecode/x076.py index fc8b1672..29667f62 100644 --- a/libs/common/unidecode/x076.py +++ b/libs/common/unidecode/x076.py @@ -101,7 +101,7 @@ data = ( 'Xian ', # 0x63 'Jie ', # 0x64 'Zheng ', # 0x65 -'[?] ', # 0x66 +None, # 0x66 'Li ', # 0x67 'Huo ', # 0x68 'Lai ', # 0x69 @@ -118,7 +118,7 @@ data = ( 'Luan ', # 0x74 'Luan ', # 0x75 'Bo ', # 0x76 -'[?] ', # 0x77 +None, # 0x77 'Gui ', # 0x78 'Po ', # 0x79 'Fa ', # 0x7a diff --git a/libs/common/unidecode/x077.py b/libs/common/unidecode/x077.py index 3ed6a36b..347afbdc 100644 --- a/libs/common/unidecode/x077.py +++ b/libs/common/unidecode/x077.py @@ -162,7 +162,7 @@ data = ( 'Cheng ', # 0xa0 'Ji ', # 0xa1 'Meng ', # 0xa2 -'[?] ', # 0xa3 +None, # 0xa3 'Run ', # 0xa4 'Pie ', # 0xa5 'Xi ', # 0xa6 diff --git a/libs/common/unidecode/x078.py b/libs/common/unidecode/x078.py index 23d677de..e07980e1 100644 --- a/libs/common/unidecode/x078.py +++ b/libs/common/unidecode/x078.py @@ -26,7 +26,7 @@ data = ( 'Dun ', # 0x18 'Pan ', # 0x19 'Yan ', # 0x1a -'[?] ', # 0x1b +None, # 0x1b 'Feng ', # 0x1c 'Fa ', # 0x1d 'Mo ', # 0x1e @@ -60,7 +60,7 @@ data = ( 'Li ', # 0x3a 'Long ', # 0x3b 'Tong ', # 0x3c -'[?] ', # 0x3d +None, # 0x3d 'Li ', # 0x3e 'Aragane ', # 0x3f 'Chu ', # 0x40 @@ -82,15 +82,15 @@ data = ( 'Tong ', # 0x50 'Peng ', # 0x51 'Xi ', # 0x52 -'[?] ', # 0x53 +None, # 0x53 'Hong ', # 0x54 'Shuo ', # 0x55 'Xia ', # 0x56 'Qiao ', # 0x57 -'[?] ', # 0x58 +None, # 0x58 'Wei ', # 0x59 'Qiao ', # 0x5a -'[?] ', # 0x5b +None, # 0x5b 'Keng ', # 0x5c 'Xiao ', # 0x5d 'Que ', # 0x5e @@ -114,7 +114,7 @@ data = ( 'Sha ', # 0x70 'Kun ', # 0x71 'Yu ', # 0x72 -'[?] ', # 0x73 +None, # 0x73 'Kaki ', # 0x74 'Lu ', # 0x75 'Chen ', # 0x76 @@ -182,7 +182,7 @@ data = ( 'Cha ', # 0xb4 'Seki ', # 0xb5 'Qi ', # 0xb6 -'[?] ', # 0xb7 +None, # 0xb7 'Feng ', # 0xb8 'Xuan ', # 0xb9 'Que ', # 0xba @@ -214,7 +214,7 @@ data = ( 'Zhe ', # 0xd4 'Ke ', # 0xd5 'La ', # 0xd6 -'[?] ', # 0xd7 +None, # 0xd7 'Qing ', # 0xd8 'Gun ', # 0xd9 'Zhuan ', # 0xda @@ -237,7 +237,7 @@ data = ( 'Zong ', # 0xeb 'Qing ', # 0xec 'Chuo ', # 0xed -'[?] ', # 0xee +None, # 0xee 'Ji ', # 0xef 'Shan ', # 0xf0 'Lao ', # 0xf1 diff --git a/libs/common/unidecode/x079.py b/libs/common/unidecode/x079.py index ed1c5143..130ed608 100644 --- a/libs/common/unidecode/x079.py +++ b/libs/common/unidecode/x079.py @@ -1,7 +1,7 @@ data = ( 'Tani ', # 0x00 'Jiao ', # 0x01 -'[?] ', # 0x02 +None, # 0x02 'Zhang ', # 0x03 'Qiao ', # 0x04 'Dun ', # 0x05 @@ -32,8 +32,8 @@ data = ( 'Meng ', # 0x1e 'Pao ', # 0x1f 'Ci ', # 0x20 -'[?] ', # 0x21 -'[?] ', # 0x22 +None, # 0x21 +None, # 0x22 'Mie ', # 0x23 'Ca ', # 0x24 'Xian ', # 0x25 @@ -76,7 +76,7 @@ data = ( 'Beng ', # 0x4a 'Dui ', # 0x4b 'Zhong ', # 0x4c -'[?] ', # 0x4d +None, # 0x4d 'Yi ', # 0x4e 'Shi ', # 0x4f 'You ', # 0x50 @@ -152,7 +152,7 @@ data = ( 'Mei ', # 0x96 'Si ', # 0x97 'Di ', # 0x98 -'[?] ', # 0x99 +None, # 0x99 'Zhuo ', # 0x9a 'Zhen ', # 0x9b 'Yong ', # 0x9c @@ -162,7 +162,7 @@ data = ( 'Si ', # 0xa0 'Ma ', # 0xa1 'Ta ', # 0xa2 -'[?] ', # 0xa3 +None, # 0xa3 'Xuan ', # 0xa4 'Qi ', # 0xa5 'Yu ', # 0xa6 diff --git a/libs/common/unidecode/x07a.py b/libs/common/unidecode/x07a.py index b6d512cd..856b1cc1 100644 --- a/libs/common/unidecode/x07a.py +++ b/libs/common/unidecode/x07a.py @@ -36,7 +36,7 @@ data = ( 'Yu ', # 0x22 'Su ', # 0x23 'Lue ', # 0x24 -'[?] ', # 0x25 +None, # 0x25 'Yi ', # 0x26 'Xi ', # 0x27 'Bian ', # 0x28 @@ -81,7 +81,7 @@ data = ( 'Wen ', # 0x4f 'Qiu ', # 0x50 'Se ', # 0x51 -'[?] ', # 0x52 +None, # 0x52 'Yi ', # 0x53 'Huang ', # 0x54 'Qie ', # 0x55 @@ -110,7 +110,7 @@ data = ( 'Gong ', # 0x6c 'Lu ', # 0x6d 'Biao ', # 0x6e -'[?] ', # 0x6f +None, # 0x6f 'Rang ', # 0x70 'Zhuo ', # 0x71 'Li ', # 0x72 @@ -166,7 +166,7 @@ data = ( 'Guan ', # 0xa4 'Kui ', # 0xa5 'Dou ', # 0xa6 -'[?] ', # 0xa7 +None, # 0xa7 'Yin ', # 0xa8 'Wo ', # 0xa9 'Wa ', # 0xaa @@ -188,7 +188,7 @@ data = ( 'Kui ', # 0xba 'Chuang ', # 0xbb 'Zhao ', # 0xbc -'[?] ', # 0xbd +None, # 0xbd 'Kuan ', # 0xbe 'Long ', # 0xbf 'Cheng ', # 0xc0 diff --git a/libs/common/unidecode/x07b.py b/libs/common/unidecode/x07b.py index c904395c..e3aa7a6b 100644 --- a/libs/common/unidecode/x07b.py +++ b/libs/common/unidecode/x07b.py @@ -207,7 +207,7 @@ data = ( 'Qiu ', # 0xcd 'Miao ', # 0xce 'Qian ', # 0xcf -'[?] ', # 0xd0 +None, # 0xd0 'Kui ', # 0xd1 'Sik ', # 0xd2 'Lou ', # 0xd3 diff --git a/libs/common/unidecode/x07c.py b/libs/common/unidecode/x07c.py index 3379947a..f4580504 100644 --- a/libs/common/unidecode/x07c.py +++ b/libs/common/unidecode/x07c.py @@ -46,7 +46,7 @@ data = ( 'Du ', # 0x2c 'Shi ', # 0x2d 'Zan ', # 0x2e -'[?] ', # 0x2f +None, # 0x2f 'Pai ', # 0x30 'Hata ', # 0x31 'Pai ', # 0x32 @@ -65,7 +65,7 @@ data = ( 'Bo ', # 0x3f 'Zhou ', # 0x40 'Lai ', # 0x41 -'[?] ', # 0x42 +None, # 0x42 'Lan ', # 0x43 'Kui ', # 0x44 'Yu ', # 0x45 @@ -77,7 +77,7 @@ data = ( 'Mi ', # 0x4b 'Chou ', # 0x4c 'Ji ', # 0x4d -'[?] ', # 0x4e +None, # 0x4e 'Hata ', # 0x4f 'Teng ', # 0x50 'Zhuan ', # 0x51 @@ -127,7 +127,7 @@ data = ( 'Zi ', # 0x7d 'Ni ', # 0x7e 'Cun ', # 0x7f -'[?] ', # 0x80 +None, # 0x80 'Qian ', # 0x81 'Kume ', # 0x82 'Bi ', # 0x83 @@ -139,7 +139,7 @@ data = ( 'Fen ', # 0x89 'Bi ', # 0x8a 'Cui ', # 0x8b -'[?] ', # 0x8c +None, # 0x8c 'Li ', # 0x8d 'Chi ', # 0x8e 'Nukamiso ', # 0x8f @@ -168,10 +168,10 @@ data = ( 'Lin ', # 0xa6 'Zhuang ', # 0xa7 'Bai ', # 0xa8 -'[?] ', # 0xa9 +None, # 0xa9 'Fen ', # 0xaa 'Ji ', # 0xab -'[?] ', # 0xac +None, # 0xac 'Sukumo ', # 0xad 'Liang ', # 0xae 'Xian ', # 0xaf @@ -235,7 +235,7 @@ data = ( 'Kuai ', # 0xe9 'Bo ', # 0xea 'Huan ', # 0xeb -'[?] ', # 0xec +None, # 0xec 'Zong ', # 0xed 'Xian ', # 0xee 'Nuo ', # 0xef diff --git a/libs/common/unidecode/x07e.py b/libs/common/unidecode/x07e.py index 131ef35c..e302f2a8 100644 --- a/libs/common/unidecode/x07e.py +++ b/libs/common/unidecode/x07e.py @@ -235,7 +235,7 @@ data = ( 'Ji ', # 0xe9 'Xu ', # 0xea 'Ling ', # 0xeb -'[?] ', # 0xec +None, # 0xec 'Xu ', # 0xed 'Qi ', # 0xee 'Fei ', # 0xef diff --git a/libs/common/unidecode/x07f.py b/libs/common/unidecode/x07f.py index 0a708d6d..994f4701 100644 --- a/libs/common/unidecode/x07f.py +++ b/libs/common/unidecode/x07f.py @@ -63,7 +63,7 @@ data = ( 'Bo ', # 0x3d 'Ping ', # 0x3e 'Hou ', # 0x3f -'[?] ', # 0x40 +None, # 0x40 'Gang ', # 0x41 'Ying ', # 0x42 'Ying ', # 0x43 @@ -85,7 +85,7 @@ data = ( 'Gang ', # 0x53 'Wang ', # 0x54 'Han ', # 0x55 -'[?] ', # 0x56 +None, # 0x56 'Luo ', # 0x57 'Fu ', # 0x58 'Mi ', # 0x59 @@ -245,7 +245,7 @@ data = ( 'Yi ', # 0xf3 'Lian ', # 0xf4 'Qu ', # 0xf5 -'[?] ', # 0xf6 +None, # 0xf6 'Lin ', # 0xf7 'Pen ', # 0xf8 'Qiao ', # 0xf9 diff --git a/libs/common/unidecode/x080.py b/libs/common/unidecode/x080.py index 11f324b4..145ab01a 100644 --- a/libs/common/unidecode/x080.py +++ b/libs/common/unidecode/x080.py @@ -1,7 +1,7 @@ data = ( 'Yao ', # 0x00 'Lao ', # 0x01 -'[?] ', # 0x02 +None, # 0x02 'Kao ', # 0x03 'Mao ', # 0x04 'Zhe ', # 0x05 @@ -64,7 +64,7 @@ data = ( 'Hong ', # 0x3e 'Geng ', # 0x3f 'Zhi ', # 0x40 -'[?] ', # 0x41 +None, # 0x41 'Nie ', # 0x42 'Dan ', # 0x43 'Zhen ', # 0x44 @@ -82,7 +82,7 @@ data = ( 'Ya ', # 0x50 'Die ', # 0x51 'Gua ', # 0x52 -'[?] ', # 0x53 +None, # 0x53 'Lian ', # 0x54 'Hao ', # 0x55 'Sheng ', # 0x56 @@ -98,7 +98,7 @@ data = ( 'Ping ', # 0x60 'Cong ', # 0x61 'Shikato ', # 0x62 -'[?] ', # 0x63 +None, # 0x63 'Ting ', # 0x64 'Yu ', # 0x65 'Cong ', # 0x66 diff --git a/libs/common/unidecode/x081.py b/libs/common/unidecode/x081.py index 01ca95d3..38817811 100644 --- a/libs/common/unidecode/x081.py +++ b/libs/common/unidecode/x081.py @@ -11,7 +11,7 @@ data = ( 'Mai ', # 0x09 'Ji ', # 0x0a 'Obiyaakasu ', # 0x0b -'[?] ', # 0x0c +None, # 0x0c 'Kuai ', # 0x0d 'Sa ', # 0x0e 'Zang ', # 0x0f diff --git a/libs/common/unidecode/x082.py b/libs/common/unidecode/x082.py index daea2e23..890f63ea 100644 --- a/libs/common/unidecode/x082.py +++ b/libs/common/unidecode/x082.py @@ -35,7 +35,7 @@ data = ( 'Gang ', # 0x21 'Shan ', # 0x22 'Yi ', # 0x23 -'[?] ', # 0x24 +None, # 0x24 'Pa ', # 0x25 'Tai ', # 0x26 'Fan ', # 0x27 @@ -62,7 +62,7 @@ data = ( 'Hong ', # 0x3c 'Pang ', # 0x3d 'Xi ', # 0x3e -'[?] ', # 0x3f +None, # 0x3f 'Fu ', # 0x40 'Zao ', # 0x41 'Feng ', # 0x42 @@ -71,7 +71,7 @@ data = ( 'Yu ', # 0x45 'Lang ', # 0x46 'Ting ', # 0x47 -'[?] ', # 0x48 +None, # 0x48 'Wei ', # 0x49 'Bo ', # 0x4a 'Meng ', # 0x4b @@ -83,7 +83,7 @@ data = ( 'Bian ', # 0x51 'Mao ', # 0x52 'Die ', # 0x53 -'[?] ', # 0x54 +None, # 0x54 'Bang ', # 0x55 'Cha ', # 0x56 'Yi ', # 0x57 diff --git a/libs/common/unidecode/x083.py b/libs/common/unidecode/x083.py index 672cd5d8..703a215c 100644 --- a/libs/common/unidecode/x083.py +++ b/libs/common/unidecode/x083.py @@ -15,9 +15,9 @@ data = ( 'Ji ', # 0x0d 'Jing ', # 0x0e 'Long ', # 0x0f -'[?] ', # 0x10 +None, # 0x10 'Niao ', # 0x11 -'[?] ', # 0x12 +None, # 0x12 'Xue ', # 0x13 'Ying ', # 0x14 'Qiong ', # 0x15 @@ -61,7 +61,7 @@ data = ( 'Mang ', # 0x3b 'Tong ', # 0x3c 'Zhong ', # 0x3d -'[?] ', # 0x3e +None, # 0x3e 'Zhu ', # 0x3f 'Xun ', # 0x40 'Huan ', # 0x41 @@ -97,7 +97,7 @@ data = ( 'Hui ', # 0x5f 'Qi ', # 0x60 'Dang ', # 0x61 -'[?] ', # 0x62 +None, # 0x62 'Rong ', # 0x63 'Hun ', # 0x64 'Ying ', # 0x65 diff --git a/libs/common/unidecode/x084.py b/libs/common/unidecode/x084.py index 571a3607..703936cb 100644 --- a/libs/common/unidecode/x084.py +++ b/libs/common/unidecode/x084.py @@ -29,12 +29,12 @@ data = ( 'Jiu ', # 0x1b 'Tie ', # 0x1c 'Luo ', # 0x1d -'[?] ', # 0x1e -'[?] ', # 0x1f +None, # 0x1e +None, # 0x1f 'Meng ', # 0x20 -'[?] ', # 0x21 +None, # 0x21 'Yaji ', # 0x22 -'[?] ', # 0x23 +None, # 0x23 'Ying ', # 0x24 'Ying ', # 0x25 'Ying ', # 0x26 @@ -137,12 +137,12 @@ data = ( 'Chan ', # 0x87 'Kai ', # 0x88 'Kui ', # 0x89 -'[?] ', # 0x8a +None, # 0x8a 'Jiang ', # 0x8b 'Lou ', # 0x8c 'Wei ', # 0x8d 'Pai ', # 0x8e -'[?] ', # 0x8f +None, # 0x8f 'Sou ', # 0x90 'Yin ', # 0x91 'Shi ', # 0x92 @@ -221,13 +221,13 @@ data = ( 'Ce ', # 0xdb 'Hai ', # 0xdc 'Lan ', # 0xdd -'[?] ', # 0xde +None, # 0xde 'Ji ', # 0xdf 'Li ', # 0xe0 'Can ', # 0xe1 'Lang ', # 0xe2 'Yu ', # 0xe3 -'[?] ', # 0xe4 +None, # 0xe4 'Ying ', # 0xe5 'Mo ', # 0xe6 'Diao ', # 0xe7 diff --git a/libs/common/unidecode/x085.py b/libs/common/unidecode/x085.py index c11c513b..5b3bfa0a 100644 --- a/libs/common/unidecode/x085.py +++ b/libs/common/unidecode/x085.py @@ -112,7 +112,7 @@ data = ( 'Xi ', # 0x6e 'Long ', # 0x6f 'Yun ', # 0x70 -'[?] ', # 0x71 +None, # 0x71 'Qi ', # 0x72 'Jian ', # 0x73 'Yun ', # 0x74 @@ -211,7 +211,7 @@ data = ( 'Qiong ', # 0xd1 'Qie ', # 0xd2 'Xian ', # 0xd3 -'[?] ', # 0xd4 +None, # 0xd4 'Ou ', # 0xd5 'Xian ', # 0xd6 'Su ', # 0xd7 @@ -241,10 +241,10 @@ data = ( 'Wei ', # 0xef 'Liu ', # 0xf0 'Hui ', # 0xf1 -'[?] ', # 0xf2 +None, # 0xf2 'Gao ', # 0xf3 'Yun ', # 0xf4 -'[?] ', # 0xf5 +None, # 0xf5 'Li ', # 0xf6 'Shu ', # 0xf7 'Chu ', # 0xf8 diff --git a/libs/common/unidecode/x086.py b/libs/common/unidecode/x086.py index 38784a61..bd094e1a 100644 --- a/libs/common/unidecode/x086.py +++ b/libs/common/unidecode/x086.py @@ -20,7 +20,7 @@ data = ( 'Hagi ', # 0x12 'Su ', # 0x13 'Jiong ', # 0x14 -'[?] ', # 0x15 +None, # 0x15 'Nie ', # 0x16 'Bo ', # 0x17 'Rang ', # 0x18 @@ -68,7 +68,7 @@ data = ( 'Lu ', # 0x42 'Jian ', # 0x43 'San ', # 0x44 -'[?] ', # 0x45 +None, # 0x45 'Lei ', # 0x46 'Quan ', # 0x47 'Xiao ', # 0x48 @@ -113,7 +113,7 @@ data = ( 'Qiu ', # 0x6f 'Cheng ', # 0x70 'Shi ', # 0x71 -'[?] ', # 0x72 +None, # 0x72 'Di ', # 0x73 'Zhe ', # 0x74 'She ', # 0x75 diff --git a/libs/common/unidecode/x087.py b/libs/common/unidecode/x087.py index 2a2b79aa..6875ac8f 100644 --- a/libs/common/unidecode/x087.py +++ b/libs/common/unidecode/x087.py @@ -73,7 +73,7 @@ data = ( 'Ying ', # 0x47 'Guo ', # 0x48 'Chan ', # 0x49 -'[?] ', # 0x4a +None, # 0x4a 'La ', # 0x4b 'Ke ', # 0x4c 'Ji ', # 0x4d @@ -128,7 +128,7 @@ data = ( 'Rong ', # 0x7e 'Ying ', # 0x7f 'Jiang ', # 0x80 -'[?] ', # 0x81 +None, # 0x81 'Lang ', # 0x82 'Pang ', # 0x83 'Si ', # 0x84 @@ -168,7 +168,7 @@ data = ( 'So ', # 0xa6 'Ebi ', # 0xa7 'Man ', # 0xa8 -'[?] ', # 0xa9 +None, # 0xa9 'Shang ', # 0xaa 'Zhe ', # 0xab 'Cao ', # 0xac @@ -244,7 +244,7 @@ data = ( 'Chong ', # 0xf2 'Xun ', # 0xf3 'Si ', # 0xf4 -'[?] ', # 0xf5 +None, # 0xf5 'Cheng ', # 0xf6 'Dang ', # 0xf7 'Li ', # 0xf8 diff --git a/libs/common/unidecode/x088.py b/libs/common/unidecode/x088.py index f907ce92..bca16854 100644 --- a/libs/common/unidecode/x088.py +++ b/libs/common/unidecode/x088.py @@ -51,7 +51,7 @@ data = ( 'Gu ', # 0x31 'Juan ', # 0x32 'Ying ', # 0x33 -'[?] ', # 0x34 +None, # 0x34 'Xi ', # 0x35 'Can ', # 0x36 'Qu ', # 0x37 @@ -79,7 +79,7 @@ data = ( 'Yan ', # 0x4d 'Kan ', # 0x4e 'Yuan ', # 0x4f -'[?] ', # 0x50 +None, # 0x50 'Ling ', # 0x51 'Xuan ', # 0x52 'Shu ', # 0x53 @@ -198,7 +198,7 @@ data = ( 'Yuki ', # 0xc4 'Zhuang ', # 0xc5 'Dang ', # 0xc6 -'[?] ', # 0xc7 +None, # 0xc7 'Kun ', # 0xc8 'Ken ', # 0xc9 'Niao ', # 0xca diff --git a/libs/common/unidecode/x089.py b/libs/common/unidecode/x089.py index d23abc3c..e223cad0 100644 --- a/libs/common/unidecode/x089.py +++ b/libs/common/unidecode/x089.py @@ -103,7 +103,7 @@ data = ( 'Pu ', # 0x65 'Ru ', # 0x66 'Zhi ', # 0x67 -'[?] ', # 0x68 +None, # 0x68 'Shu ', # 0x69 'Wa ', # 0x6a 'Shi ', # 0x6b @@ -131,7 +131,7 @@ data = ( 'Yao ', # 0x81 'Feng ', # 0x82 'Tan ', # 0x83 -'[?] ', # 0x84 +None, # 0x84 'Biao ', # 0x85 'Fu ', # 0x86 'Ba ', # 0x87 diff --git a/libs/common/unidecode/x08b.py b/libs/common/unidecode/x08b.py index b89c37a2..f3c7d374 100644 --- a/libs/common/unidecode/x08b.py +++ b/libs/common/unidecode/x08b.py @@ -2,7 +2,7 @@ data = ( 'Mou ', # 0x00 'Ye ', # 0x01 'Wei ', # 0x02 -'[?] ', # 0x03 +None, # 0x03 'Teng ', # 0x04 'Zou ', # 0x05 'Shan ', # 0x06 @@ -33,7 +33,7 @@ data = ( 'Tao ', # 0x1f 'Yao ', # 0x20 'Yao ', # 0x21 -'[?] ', # 0x22 +None, # 0x22 'Yu ', # 0x23 'Biao ', # 0x24 'Cong ', # 0x25 diff --git a/libs/common/unidecode/x08c.py b/libs/common/unidecode/x08c.py index 514c446b..3735ef71 100644 --- a/libs/common/unidecode/x08c.py +++ b/libs/common/unidecode/x08c.py @@ -109,7 +109,7 @@ data = ( 'Yu ', # 0x6b 'Zhu ', # 0x6c 'Jia ', # 0x6d -'[?] ', # 0x6e +None, # 0x6e 'Xi ', # 0x6f 'Bo ', # 0x70 'Wen ', # 0x71 diff --git a/libs/common/unidecode/x08d.py b/libs/common/unidecode/x08d.py index ae63f226..f6cbadde 100644 --- a/libs/common/unidecode/x08d.py +++ b/libs/common/unidecode/x08d.py @@ -11,7 +11,7 @@ data = ( 'Tan ', # 0x09 'Zan ', # 0x0a 'Yan ', # 0x0b -'[?] ', # 0x0c +None, # 0x0c 'Shan ', # 0x0d 'Wan ', # 0x0e 'Ying ', # 0x0f @@ -23,7 +23,7 @@ data = ( 'Du ', # 0x15 'Shu ', # 0x16 'Yan ', # 0x17 -'[?] ', # 0x18 +None, # 0x18 'Xuan ', # 0x19 'Long ', # 0x1a 'Gan ', # 0x1b @@ -175,7 +175,7 @@ data = ( 'Yao ', # 0xad 'Zao ', # 0xae 'Ti ', # 0xaf -'[?] ', # 0xb0 +None, # 0xb0 'Zan ', # 0xb1 'Zan ', # 0xb2 'Zu ', # 0xb3 diff --git a/libs/common/unidecode/x08e.py b/libs/common/unidecode/x08e.py index 015ed1ea..628faf2b 100644 --- a/libs/common/unidecode/x08e.py +++ b/libs/common/unidecode/x08e.py @@ -13,7 +13,7 @@ data = ( 'Jiao ', # 0x0b 'Chou ', # 0x0c 'Qiao ', # 0x0d -'[?] ', # 0x0e +None, # 0x0e 'Ta ', # 0x0f 'Jian ', # 0x10 'Qi ', # 0x11 @@ -187,7 +187,7 @@ data = ( 'Ju ', # 0xb9 'Tang ', # 0xba 'Utsuke ', # 0xbb -'[?] ', # 0xbc +None, # 0xbc 'Yan ', # 0xbd 'Shitsuke ', # 0xbe 'Kang ', # 0xbf diff --git a/libs/common/unidecode/x08f.py b/libs/common/unidecode/x08f.py index 39d04a9c..d52164bc 100644 --- a/libs/common/unidecode/x08f.py +++ b/libs/common/unidecode/x08f.py @@ -169,7 +169,7 @@ data = ( 'Bian ', # 0xa7 'Bian ', # 0xa8 'Bian ', # 0xa9 -'[?] ', # 0xaa +None, # 0xaa 'Bian ', # 0xab 'Ban ', # 0xac 'Ci ', # 0xad diff --git a/libs/common/unidecode/x090.py b/libs/common/unidecode/x090.py index ade65069..e8b1eee1 100644 --- a/libs/common/unidecode/x090.py +++ b/libs/common/unidecode/x090.py @@ -99,7 +99,7 @@ data = ( 'Su ', # 0x61 'Ta ', # 0x62 'Qian ', # 0x63 -'[?] ', # 0x64 +None, # 0x64 'Yao ', # 0x65 'Guan ', # 0x66 'Zhang ', # 0x67 @@ -155,7 +155,7 @@ data = ( 'Mang ', # 0x99 'Ru ', # 0x9a 'Qiong ', # 0x9b -'[?] ', # 0x9c +None, # 0x9c 'Kuang ', # 0x9d 'Fu ', # 0x9e 'Kang ', # 0x9f diff --git a/libs/common/unidecode/x091.py b/libs/common/unidecode/x091.py index fa3dd766..764e3e4d 100644 --- a/libs/common/unidecode/x091.py +++ b/libs/common/unidecode/x091.py @@ -91,7 +91,7 @@ data = ( 'Zhen ', # 0x59 'Fen ', # 0x5a 'Sakenomoto ', # 0x5b -'[?] ', # 0x5c +None, # 0x5c 'Yun ', # 0x5d 'Tai ', # 0x5e 'Tian ', # 0x5f diff --git a/libs/common/unidecode/x092.py b/libs/common/unidecode/x092.py index e752f4fe..da9cccd2 100644 --- a/libs/common/unidecode/x092.py +++ b/libs/common/unidecode/x092.py @@ -42,7 +42,7 @@ data = ( 'Habaki ', # 0x28 'Irori ', # 0x29 'Ngaak ', # 0x2a -'[?] ', # 0x2b +None, # 0x2b 'Duo ', # 0x2c 'Zi ', # 0x2d 'Ni ', # 0x2e @@ -243,7 +243,7 @@ data = ( 'Te ', # 0xf1 'Pyeng ', # 0xf2 'Zhu ', # 0xf3 -'[?] ', # 0xf4 +None, # 0xf4 'Tu ', # 0xf5 'Liu ', # 0xf6 'Zui ', # 0xf7 diff --git a/libs/common/unidecode/x093.py b/libs/common/unidecode/x093.py index 82857e9a..eabec27f 100644 --- a/libs/common/unidecode/x093.py +++ b/libs/common/unidecode/x093.py @@ -62,13 +62,13 @@ data = ( 'Nai ', # 0x3c 'Wan ', # 0x3d 'Zan ', # 0x3e -'[?] ', # 0x3f +None, # 0x3f 'De ', # 0x40 'Xian ', # 0x41 -'[?] ', # 0x42 +None, # 0x42 'Huo ', # 0x43 'Liang ', # 0x44 -'[?] ', # 0x45 +None, # 0x45 'Men ', # 0x46 'Kai ', # 0x47 'Ying ', # 0x48 @@ -133,7 +133,7 @@ data = ( 'Pai ', # 0x83 'Ai ', # 0x84 'Jie ', # 0x85 -'[?] ', # 0x86 +None, # 0x86 'Mei ', # 0x87 'Chuo ', # 0x88 'Ta ', # 0x89 @@ -187,9 +187,9 @@ data = ( 'Kasugai ', # 0xb9 'Habaki ', # 0xba 'Suo ', # 0xbb -'[?] ', # 0xbc -'[?] ', # 0xbd -'[?] ', # 0xbe +None, # 0xbc +None, # 0xbd +None, # 0xbe 'Na ', # 0xbf 'Lu ', # 0xc0 'Suo ', # 0xc1 @@ -238,10 +238,10 @@ data = ( 'Xia ', # 0xec 'Xi ', # 0xed 'Kang ', # 0xee -'[?] ', # 0xef +None, # 0xef 'Beng ', # 0xf0 -'[?] ', # 0xf1 -'[?] ', # 0xf2 +None, # 0xf1 +None, # 0xf2 'Zheng ', # 0xf3 'Lu ', # 0xf4 'Hua ', # 0xf5 diff --git a/libs/common/unidecode/x094.py b/libs/common/unidecode/x094.py index 17eb9ddb..990ab60c 100644 --- a/libs/common/unidecode/x094.py +++ b/libs/common/unidecode/x094.py @@ -33,8 +33,8 @@ data = ( 'Ti ', # 0x1f 'Pu ', # 0x20 'Tie ', # 0x21 -'[?] ', # 0x22 -'[?] ', # 0x23 +None, # 0x22 +None, # 0x23 'Ding ', # 0x24 'Shan ', # 0x25 'Kai ', # 0x26 @@ -101,8 +101,8 @@ data = ( 'Biao ', # 0x63 'Bao ', # 0x64 'Lu ', # 0x65 -'[?] ', # 0x66 -'[?] ', # 0x67 +None, # 0x66 +None, # 0x67 'Long ', # 0x68 'E ', # 0x69 'Lu ', # 0x6a diff --git a/libs/common/unidecode/x095.py b/libs/common/unidecode/x095.py index 4b363942..71b7e4ba 100644 --- a/libs/common/unidecode/x095.py +++ b/libs/common/unidecode/x095.py @@ -205,7 +205,7 @@ data = ( 'Que ', # 0xcb 'Lan ', # 0xcc 'Du ', # 0xcd -'[?] ', # 0xce +None, # 0xce 'Phwung ', # 0xcf 'Tian ', # 0xd0 'Nie ', # 0xd1 @@ -230,7 +230,7 @@ data = ( 'Huan ', # 0xe4 'Ta ', # 0xe5 'Wen ', # 0xe6 -'[?] ', # 0xe7 +None, # 0xe7 'Men ', # 0xe8 'Shuan ', # 0xe9 'Shan ', # 0xea diff --git a/libs/common/unidecode/x096.py b/libs/common/unidecode/x096.py index 738a4ea3..6aaa359d 100644 --- a/libs/common/unidecode/x096.py +++ b/libs/common/unidecode/x096.py @@ -160,7 +160,7 @@ data = ( 'Ao ', # 0x9e 'Xi ', # 0x9f 'Yin ', # 0xa0 -'[?] ', # 0xa1 +None, # 0xa1 'Rao ', # 0xa2 'Lin ', # 0xa3 'Tui ', # 0xa4 diff --git a/libs/common/unidecode/x097.py b/libs/common/unidecode/x097.py index 7255f0f8..66e08e90 100644 --- a/libs/common/unidecode/x097.py +++ b/libs/common/unidecode/x097.py @@ -22,7 +22,7 @@ data = ( 'Chou ', # 0x14 'Tun ', # 0x15 'Lin ', # 0x16 -'[?] ', # 0x17 +None, # 0x17 'Dong ', # 0x18 'Ying ', # 0x19 'Wu ', # 0x1a @@ -58,7 +58,7 @@ data = ( 'Ba ', # 0x38 'Pi ', # 0x39 'Wei ', # 0x3a -'[?] ', # 0x3b +None, # 0x3b 'Xi ', # 0x3c 'Ji ', # 0x3d 'Mai ', # 0x3e @@ -76,7 +76,7 @@ data = ( 'Feng ', # 0x4a 'Li ', # 0x4b 'Bao ', # 0x4c -'[?] ', # 0x4d +None, # 0x4d 'He ', # 0x4e 'He ', # 0x4f 'Bing ', # 0x50 @@ -212,7 +212,7 @@ data = ( 'Qiao ', # 0xd2 'Han ', # 0xd3 'Chang ', # 0xd4 -'[?] ', # 0xd5 +None, # 0xd5 'Rou ', # 0xd6 'Xun ', # 0xd7 'She ', # 0xd8 @@ -222,7 +222,7 @@ data = ( 'Tao ', # 0xdc 'Gou ', # 0xdd 'Yun ', # 0xde -'[?] ', # 0xdf +None, # 0xdf 'Bi ', # 0xe0 'Wei ', # 0xe1 'Hui ', # 0xe2 diff --git a/libs/common/unidecode/x098.py b/libs/common/unidecode/x098.py index 98160e77..298a1fbd 100644 --- a/libs/common/unidecode/x098.py +++ b/libs/common/unidecode/x098.py @@ -201,7 +201,7 @@ data = ( 'Biao ', # 0xc7 'Biao ', # 0xc8 'Liao ', # 0xc9 -'[?] ', # 0xca +None, # 0xca 'Se ', # 0xcb 'Feng ', # 0xcc 'Biao ', # 0xcd diff --git a/libs/common/unidecode/x099.py b/libs/common/unidecode/x099.py index 2adf3de8..60a68014 100644 --- a/libs/common/unidecode/x099.py +++ b/libs/common/unidecode/x099.py @@ -198,7 +198,7 @@ data = ( 'Tuo ', # 0xc4 'Yi ', # 0xc5 'Qu ', # 0xc6 -'[?] ', # 0xc7 +None, # 0xc7 'Qu ', # 0xc8 'Jiong ', # 0xc9 'Bo ', # 0xca diff --git a/libs/common/unidecode/x09b.py b/libs/common/unidecode/x09b.py index 7fd0a2db..24dfb99e 100644 --- a/libs/common/unidecode/x09b.py +++ b/libs/common/unidecode/x09b.py @@ -151,7 +151,7 @@ data = ( 'Gu ', # 0x95 'Kajika ', # 0x96 'Tong ', # 0x97 -'[?] ', # 0x98 +None, # 0x98 'Ta ', # 0x99 'Jie ', # 0x9a 'Shu ', # 0x9b diff --git a/libs/common/unidecode/x09d.py b/libs/common/unidecode/x09d.py index 99d5859c..c23f21a8 100644 --- a/libs/common/unidecode/x09d.py +++ b/libs/common/unidecode/x09d.py @@ -144,7 +144,7 @@ data = ( 'Kikuitadaki ', # 0x8e 'Ji ', # 0x8f 'Shu ', # 0x90 -'[?] ', # 0x91 +None, # 0x91 'Chi ', # 0x92 'Miao ', # 0x93 'Rou ', # 0x94 diff --git a/libs/common/unidecode/x09e.py b/libs/common/unidecode/x09e.py index 8a392a23..5ddf6812 100644 --- a/libs/common/unidecode/x09e.py +++ b/libs/common/unidecode/x09e.py @@ -181,7 +181,7 @@ data = ( 'Lai ', # 0xb3 'Qu ', # 0xb4 'Mian ', # 0xb5 -'[?] ', # 0xb6 +None, # 0xb6 'Feng ', # 0xb7 'Fu ', # 0xb8 'Qu ', # 0xb9 diff --git a/libs/common/unidecode/x09f.py b/libs/common/unidecode/x09f.py index acd59a69..ea8e372b 100644 --- a/libs/common/unidecode/x09f.py +++ b/libs/common/unidecode/x09f.py @@ -165,93 +165,93 @@ data = ( 'Jue ', # 0xa3 'Xie ', # 0xa4 'Yu ', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x0a4.py b/libs/common/unidecode/x0a4.py index 4fb6b264..334030e1 100644 --- a/libs/common/unidecode/x0a4.py +++ b/libs/common/unidecode/x0a4.py @@ -140,9 +140,9 @@ data = ( 'yyp', # 0x8a 'yyrx', # 0x8b 'yyr', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f +None, # 0x8d +None, # 0x8e +None, # 0x8f 'Qot', # 0x90 'Li', # 0x91 'Kit', # 0x92 @@ -161,8 +161,8 @@ data = ( 'Hxuo', # 0x9f 'Tat', # 0xa0 'Ga', # 0xa1 -'[?]', # 0xa2 -'[?]', # 0xa3 +None, # 0xa2 +None, # 0xa3 'Ddur', # 0xa4 'Bur', # 0xa5 'Gguo', # 0xa6 @@ -179,7 +179,7 @@ data = ( 'Vep', # 0xb1 'Za', # 0xb2 'Jo', # 0xb3 -'[?]', # 0xb4 +None, # 0xb4 'Jjy', # 0xb5 'Got', # 0xb6 'Jjie', # 0xb7 @@ -192,66 +192,66 @@ data = ( 'Cip', # 0xbe 'Hxop', # 0xbf 'Shat', # 0xc0 -'[?]', # 0xc1 +None, # 0xc1 'Shop', # 0xc2 'Che', # 0xc3 'Zziet', # 0xc4 -'[?]', # 0xc5 +None, # 0xc5 'Ke', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x0d7.py b/libs/common/unidecode/x0d7.py index 1dfb7296..5036de25 100644 --- a/libs/common/unidecode/x0d7.py +++ b/libs/common/unidecode/x0d7.py @@ -163,95 +163,95 @@ data = ( 'hit', # 0xa1 'hip', # 0xa2 'hih', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x0fa.py b/libs/common/unidecode/x0fa.py index 3d4e705c..24ff84d1 100644 --- a/libs/common/unidecode/x0fa.py +++ b/libs/common/unidecode/x0fa.py @@ -13,13 +13,13 @@ data = ( 'Hwak ', # 0x0b 'Wu ', # 0x0c 'Huo ', # 0x0d -'[?] ', # 0x0e -'[?] ', # 0x0f +None, # 0x0e +None, # 0x0f 'Zhong ', # 0x10 -'[?] ', # 0x11 +None, # 0x11 'Qing ', # 0x12 -'[?] ', # 0x13 -'[?] ', # 0x14 +None, # 0x13 +None, # 0x14 'Xi ', # 0x15 'Zhu ', # 0x16 'Yi ', # 0x17 @@ -30,228 +30,228 @@ data = ( 'Jing ', # 0x1c 'Jing ', # 0x1d 'Yu ', # 0x1e -'[?] ', # 0x1f +None, # 0x1f 'Hagi ', # 0x20 -'[?] ', # 0x21 +None, # 0x21 'Zhu ', # 0x22 -'[?] ', # 0x23 -'[?] ', # 0x24 +None, # 0x23 +None, # 0x24 'Yi ', # 0x25 'Du ', # 0x26 -'[?] ', # 0x27 -'[?] ', # 0x28 -'[?] ', # 0x29 +None, # 0x27 +None, # 0x28 +None, # 0x29 'Fan ', # 0x2a 'Si ', # 0x2b 'Guan ', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f -'[?]', # 0x30 -'[?]', # 0x31 -'[?]', # 0x32 -'[?]', # 0x33 -'[?]', # 0x34 -'[?]', # 0x35 -'[?]', # 0x36 -'[?]', # 0x37 -'[?]', # 0x38 -'[?]', # 0x39 -'[?]', # 0x3a -'[?]', # 0x3b -'[?]', # 0x3c -'[?]', # 0x3d -'[?]', # 0x3e -'[?]', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f -'[?]', # 0x50 -'[?]', # 0x51 -'[?]', # 0x52 -'[?]', # 0x53 -'[?]', # 0x54 -'[?]', # 0x55 -'[?]', # 0x56 -'[?]', # 0x57 -'[?]', # 0x58 -'[?]', # 0x59 -'[?]', # 0x5a -'[?]', # 0x5b -'[?]', # 0x5c -'[?]', # 0x5d -'[?]', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 -'[?]', # 0x61 -'[?]', # 0x62 -'[?]', # 0x63 -'[?]', # 0x64 -'[?]', # 0x65 -'[?]', # 0x66 -'[?]', # 0x67 -'[?]', # 0x68 -'[?]', # 0x69 -'[?]', # 0x6a -'[?]', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f -'[?]', # 0x70 -'[?]', # 0x71 -'[?]', # 0x72 -'[?]', # 0x73 -'[?]', # 0x74 -'[?]', # 0x75 -'[?]', # 0x76 -'[?]', # 0x77 -'[?]', # 0x78 -'[?]', # 0x79 -'[?]', # 0x7a -'[?]', # 0x7b -'[?]', # 0x7c -'[?]', # 0x7d -'[?]', # 0x7e -'[?]', # 0x7f -'[?]', # 0x80 -'[?]', # 0x81 -'[?]', # 0x82 -'[?]', # 0x83 -'[?]', # 0x84 -'[?]', # 0x85 -'[?]', # 0x86 -'[?]', # 0x87 -'[?]', # 0x88 -'[?]', # 0x89 -'[?]', # 0x8a -'[?]', # 0x8b -'[?]', # 0x8c -'[?]', # 0x8d -'[?]', # 0x8e -'[?]', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 -'[?]', # 0x92 -'[?]', # 0x93 -'[?]', # 0x94 -'[?]', # 0x95 -'[?]', # 0x96 -'[?]', # 0x97 -'[?]', # 0x98 -'[?]', # 0x99 -'[?]', # 0x9a -'[?]', # 0x9b -'[?]', # 0x9c -'[?]', # 0x9d -'[?]', # 0x9e -'[?]', # 0x9f -'[?]', # 0xa0 -'[?]', # 0xa1 -'[?]', # 0xa2 -'[?]', # 0xa3 -'[?]', # 0xa4 -'[?]', # 0xa5 -'[?]', # 0xa6 -'[?]', # 0xa7 -'[?]', # 0xa8 -'[?]', # 0xa9 -'[?]', # 0xaa -'[?]', # 0xab -'[?]', # 0xac -'[?]', # 0xad -'[?]', # 0xae -'[?]', # 0xaf -'[?]', # 0xb0 -'[?]', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 -'[?]', # 0xf9 -'[?]', # 0xfa -'[?]', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x0fb.py b/libs/common/unidecode/x0fb.py index bf69dbb9..5e098bc9 100644 --- a/libs/common/unidecode/x0fb.py +++ b/libs/common/unidecode/x0fb.py @@ -6,79 +6,79 @@ data = ( 'ffl', # 0x04 'st', # 0x05 'st', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 'mn', # 0x13 'me', # 0x14 'mi', # 0x15 'vn', # 0x16 'mkh', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'yi', # 0x1d +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +'i', # 0x1d '', # 0x1e -'ay', # 0x1f +'AY', # 0x1f '`', # 0x20 -'', # 0x21 +'A', # 0x21 'd', # 0x22 'h', # 0x23 -'k', # 0x24 +'KH', # 0x24 'l', # 0x25 'm', # 0x26 -'m', # 0x27 +'r', # 0x27 't', # 0x28 '+', # 0x29 -'sh', # 0x2a -'s', # 0x2b -'sh', # 0x2c -'s', # 0x2d +'SH', # 0x2a +'S', # 0x2b +'SH', # 0x2c +'S', # 0x2d 'a', # 0x2e 'a', # 0x2f -'', # 0x30 +'A', # 0x30 'b', # 0x31 'g', # 0x32 'd', # 0x33 'h', # 0x34 'v', # 0x35 'z', # 0x36 -'[?]', # 0x37 +None, # 0x37 't', # 0x38 'y', # 0x39 -'k', # 0x3a -'k', # 0x3b +'KH', # 0x3a +'KH', # 0x3b 'l', # 0x3c -'[?]', # 0x3d -'l', # 0x3e -'[?]', # 0x3f +None, # 0x3d +'m', # 0x3e +None, # 0x3f 'n', # 0x40 -'n', # 0x41 -'[?]', # 0x42 +'s', # 0x41 +None, # 0x42 'p', # 0x43 'p', # 0x44 -'[?]', # 0x45 -'ts', # 0x46 -'ts', # 0x47 +None, # 0x45 +'TS', # 0x46 +'k', # 0x47 'r', # 0x48 -'sh', # 0x49 +'SH', # 0x49 't', # 0x4a -'vo', # 0x4b -'b', # 0x4c -'k', # 0x4d -'p', # 0x4e -'l', # 0x4f +'o', # 0x4b +'v', # 0x4c +'KH', # 0x4d +'f', # 0x4e +'EL', # 0x4f '', # 0x50 '', # 0x51 '', # 0x52 @@ -177,39 +177,39 @@ data = ( '', # 0xaf '', # 0xb0 '', # 0xb1 -'[?]', # 0xb2 -'[?]', # 0xb3 -'[?]', # 0xb4 -'[?]', # 0xb5 -'[?]', # 0xb6 -'[?]', # 0xb7 -'[?]', # 0xb8 -'[?]', # 0xb9 -'[?]', # 0xba -'[?]', # 0xbb -'[?]', # 0xbc -'[?]', # 0xbd -'[?]', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 -'[?]', # 0xc2 -'[?]', # 0xc3 -'[?]', # 0xc4 -'[?]', # 0xc5 -'[?]', # 0xc6 -'[?]', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 '', # 0xd3 '', # 0xd4 '', # 0xd5 diff --git a/libs/common/unidecode/x0fd.py b/libs/common/unidecode/x0fd.py index 892bcb05..be0c0f95 100644 --- a/libs/common/unidecode/x0fd.py +++ b/libs/common/unidecode/x0fd.py @@ -63,22 +63,22 @@ data = ( '', # 0x3d '', # 0x3e '', # 0x3f -'[?]', # 0x40 -'[?]', # 0x41 -'[?]', # 0x42 -'[?]', # 0x43 -'[?]', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 -'[?]', # 0x49 -'[?]', # 0x4a -'[?]', # 0x4b -'[?]', # 0x4c -'[?]', # 0x4d -'[?]', # 0x4e -'[?]', # 0x4f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f '', # 0x50 '', # 0x51 '', # 0x52 @@ -143,8 +143,8 @@ data = ( '', # 0x8d '', # 0x8e '', # 0x8f -'[?]', # 0x90 -'[?]', # 0x91 +None, # 0x90 +None, # 0x91 '', # 0x92 '', # 0x93 '', # 0x94 @@ -199,46 +199,46 @@ data = ( '', # 0xc5 '', # 0xc6 '', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 -'[?]', # 0xca -'[?]', # 0xcb -'[?]', # 0xcc -'[?]', # 0xcd -'[?]', # 0xce -'[?]', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 -'[?]', # 0xd2 -'[?]', # 0xd3 -'[?]', # 0xd4 -'[?]', # 0xd5 -'[?]', # 0xd6 -'[?]', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 -'[?]', # 0xda -'[?]', # 0xdb -'[?]', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf -'[?]', # 0xe0 -'[?]', # 0xe1 -'[?]', # 0xe2 -'[?]', # 0xe3 -'[?]', # 0xe4 -'[?]', # 0xe5 -'[?]', # 0xe6 -'[?]', # 0xe7 -'[?]', # 0xe8 -'[?]', # 0xe9 -'[?]', # 0xea -'[?]', # 0xeb -'[?]', # 0xec -'[?]', # 0xed -'[?]', # 0xee -'[?]', # 0xef +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef '', # 0xf0 '', # 0xf1 '', # 0xf2 @@ -251,7 +251,7 @@ data = ( '', # 0xf9 '', # 0xfa '', # 0xfb -'[?]', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfc +None, # 0xfd +None, # 0xfe ) diff --git a/libs/common/unidecode/x0fe.py b/libs/common/unidecode/x0fe.py index 60e86179..04dbd93f 100644 --- a/libs/common/unidecode/x0fe.py +++ b/libs/common/unidecode/x0fe.py @@ -1,52 +1,52 @@ data = ( -'[?]', # 0x00 -'[?]', # 0x01 -'[?]', # 0x02 -'[?]', # 0x03 -'[?]', # 0x04 -'[?]', # 0x05 -'[?]', # 0x06 -'[?]', # 0x07 -'[?]', # 0x08 -'[?]', # 0x09 -'[?]', # 0x0a -'[?]', # 0x0b -'[?]', # 0x0c -'[?]', # 0x0d -'[?]', # 0x0e -'[?]', # 0x0f -'[?]', # 0x10 -'[?]', # 0x11 -'[?]', # 0x12 -'[?]', # 0x13 -'[?]', # 0x14 -'[?]', # 0x15 -'[?]', # 0x16 -'[?]', # 0x17 -'[?]', # 0x18 -'[?]', # 0x19 -'[?]', # 0x1a -'[?]', # 0x1b -'[?]', # 0x1c -'[?]', # 0x1d -'[?]', # 0x1e -'[?]', # 0x1f +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f '', # 0x20 '', # 0x21 '', # 0x22 '~', # 0x23 -'[?]', # 0x24 -'[?]', # 0x25 -'[?]', # 0x26 -'[?]', # 0x27 -'[?]', # 0x28 -'[?]', # 0x29 -'[?]', # 0x2a -'[?]', # 0x2b -'[?]', # 0x2c -'[?]', # 0x2d -'[?]', # 0x2e -'[?]', # 0x2f +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f '..', # 0x30 '--', # 0x31 '-', # 0x32 @@ -68,10 +68,10 @@ data = ( '] ', # 0x42 '{', # 0x43 '}', # 0x44 -'[?]', # 0x45 -'[?]', # 0x46 -'[?]', # 0x47 -'[?]', # 0x48 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 '', # 0x49 '', # 0x4a '', # 0x4b @@ -107,16 +107,16 @@ data = ( '$', # 0x69 '%', # 0x6a '@', # 0x6b -'[?]', # 0x6c -'[?]', # 0x6d -'[?]', # 0x6e -'[?]', # 0x6f +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f '', # 0x70 '', # 0x71 '', # 0x72 -'[?]', # 0x73 +None, # 0x73 '', # 0x74 -'[?]', # 0x75 +None, # 0x75 '', # 0x76 '', # 0x77 '', # 0x78 @@ -252,7 +252,7 @@ data = ( '', # 0xfa '', # 0xfb '', # 0xfc -'[?]', # 0xfd -'[?]', # 0xfe +None, # 0xfd +None, # 0xfe '', # 0xff ) diff --git a/libs/common/unidecode/x0ff.py b/libs/common/unidecode/x0ff.py index 04410151..557462c6 100644 --- a/libs/common/unidecode/x0ff.py +++ b/libs/common/unidecode/x0ff.py @@ -1,5 +1,5 @@ data = ( -'[?]', # 0x00 +None, # 0x00 '!', # 0x01 '"', # 0x02 '#', # 0x03 @@ -94,8 +94,8 @@ data = ( '|', # 0x5c '}', # 0x5d '~', # 0x5e -'[?]', # 0x5f -'[?]', # 0x60 +None, # 0x5f +None, # 0x60 '.', # 0x61 '[', # 0x62 ']', # 0x63 @@ -190,39 +190,39 @@ data = ( 't', # 0xbc 'p', # 0xbd 'h', # 0xbe -'[?]', # 0xbf -'[?]', # 0xc0 -'[?]', # 0xc1 +None, # 0xbf +None, # 0xc0 +None, # 0xc1 'a', # 0xc2 'ae', # 0xc3 'ya', # 0xc4 'yae', # 0xc5 'eo', # 0xc6 'e', # 0xc7 -'[?]', # 0xc8 -'[?]', # 0xc9 +None, # 0xc8 +None, # 0xc9 'yeo', # 0xca 'ye', # 0xcb 'o', # 0xcc 'wa', # 0xcd 'wae', # 0xce 'oe', # 0xcf -'[?]', # 0xd0 -'[?]', # 0xd1 +None, # 0xd0 +None, # 0xd1 'yo', # 0xd2 'u', # 0xd3 'weo', # 0xd4 'we', # 0xd5 'wi', # 0xd6 'yu', # 0xd7 -'[?]', # 0xd8 -'[?]', # 0xd9 +None, # 0xd8 +None, # 0xd9 'eu', # 0xda 'yi', # 0xdb 'i', # 0xdc -'[?]', # 0xdd -'[?]', # 0xde -'[?]', # 0xdf +None, # 0xdd +None, # 0xde +None, # 0xdf '/C', # 0xe0 'PS', # 0xe1 '!', # 0xe2 @@ -230,7 +230,7 @@ data = ( '|', # 0xe4 'Y=', # 0xe5 'W=', # 0xe6 -'[?]', # 0xe7 +None, # 0xe7 '|', # 0xe8 '-', # 0xe9 '|', # 0xea @@ -238,16 +238,16 @@ data = ( '|', # 0xec '#', # 0xed 'O', # 0xee -'[?]', # 0xef -'[?]', # 0xf0 -'[?]', # 0xf1 -'[?]', # 0xf2 -'[?]', # 0xf3 -'[?]', # 0xf4 -'[?]', # 0xf5 -'[?]', # 0xf6 -'[?]', # 0xf7 -'[?]', # 0xf8 +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 '{', # 0xf9 '|', # 0xfa '}', # 0xfb diff --git a/libs/common/unidecode/x1d4.py b/libs/common/unidecode/x1d4.py index 53795931..7d23fd47 100644 --- a/libs/common/unidecode/x1d4.py +++ b/libs/common/unidecode/x1d4.py @@ -84,7 +84,7 @@ data = ( 'e', # 0x52 'f', # 0x53 'g', # 0x54 -'', # 0x55 +None, # 0x55 'i', # 0x56 'j', # 0x57 'k', # 0x58 @@ -156,23 +156,23 @@ data = ( 'y', # 0x9a 'z', # 0x9b 'A', # 0x9c -'', # 0x9d +None, # 0x9d 'C', # 0x9e 'D', # 0x9f -'', # 0xa0 -'', # 0xa1 +None, # 0xa0 +None, # 0xa1 'G', # 0xa2 -'', # 0xa3 -'', # 0xa4 +None, # 0xa3 +None, # 0xa4 'J', # 0xa5 'K', # 0xa6 -'', # 0xa7 -'', # 0xa8 +None, # 0xa7 +None, # 0xa8 'N', # 0xa9 'O', # 0xaa 'P', # 0xab 'Q', # 0xac -'', # 0xad +None, # 0xad 'S', # 0xae 'T', # 0xaf 'U', # 0xb0 @@ -185,9 +185,9 @@ data = ( 'b', # 0xb7 'c', # 0xb8 'd', # 0xb9 -'', # 0xba +None, # 0xba 'f', # 0xbb -'', # 0xbc +None, # 0xbc 'h', # 0xbd 'i', # 0xbe 'j', # 0xbf @@ -195,7 +195,7 @@ data = ( 'l', # 0xc1 'm', # 0xc2 'n', # 0xc3 -'', # 0xc4 +None, # 0xc4 'p', # 0xc5 'q', # 0xc6 'r', # 0xc7 diff --git a/libs/common/unidecode/x1d5.py b/libs/common/unidecode/x1d5.py index 6ec605ba..40e6935d 100644 --- a/libs/common/unidecode/x1d5.py +++ b/libs/common/unidecode/x1d5.py @@ -5,13 +5,13 @@ data = ( 'z', # 0x03 'A', # 0x04 'B', # 0x05 -'', # 0x06 +None, # 0x06 'D', # 0x07 'E', # 0x08 'F', # 0x09 'G', # 0x0a -'', # 0x0b -'', # 0x0c +None, # 0x0b +None, # 0x0c 'J', # 0x0d 'K', # 0x0e 'L', # 0x0f @@ -20,7 +20,7 @@ data = ( 'O', # 0x12 'P', # 0x13 'Q', # 0x14 -'', # 0x15 +None, # 0x15 'S', # 0x16 'T', # 0x17 'U', # 0x18 @@ -28,7 +28,7 @@ data = ( 'W', # 0x1a 'X', # 0x1b 'Y', # 0x1c -'', # 0x1d +None, # 0x1d 'a', # 0x1e 'b', # 0x1f 'c', # 0x20 @@ -57,22 +57,22 @@ data = ( 'z', # 0x37 'A', # 0x38 'B', # 0x39 -'', # 0x3a +None, # 0x3a 'D', # 0x3b 'E', # 0x3c 'F', # 0x3d 'G', # 0x3e -'', # 0x3f +None, # 0x3f 'I', # 0x40 'J', # 0x41 'K', # 0x42 'L', # 0x43 'M', # 0x44 -'', # 0x45 +None, # 0x45 'O', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 +None, # 0x47 +None, # 0x48 +None, # 0x49 'S', # 0x4a 'T', # 0x4b 'U', # 0x4c @@ -80,7 +80,7 @@ data = ( 'W', # 0x4e 'X', # 0x4f 'Y', # 0x50 -'', # 0x51 +None, # 0x51 'a', # 0x52 'b', # 0x53 'c', # 0x54 diff --git a/libs/common/unidecode/x1d6.py b/libs/common/unidecode/x1d6.py index c06a855d..2be1a7fb 100644 --- a/libs/common/unidecode/x1d6.py +++ b/libs/common/unidecode/x1d6.py @@ -165,8 +165,8 @@ data = ( 'z', # 0xa3 'i', # 0xa4 'j', # 0xa5 -'', # 0xa6 -'', # 0xa7 +None, # 0xa6 +None, # 0xa7 'Alpha', # 0xa8 'Beta', # 0xa9 'Gamma', # 0xaa @@ -218,41 +218,41 @@ data = ( 'chi', # 0xd8 'psi', # 0xd9 'omega', # 0xda -'', # 0xdb -'', # 0xdc -'', # 0xdd -'', # 0xde -'', # 0xdf -'', # 0xe0 -'', # 0xe1 -'', # 0xe2 -'', # 0xe3 -'', # 0xe4 -'', # 0xe5 -'', # 0xe6 -'', # 0xe7 -'', # 0xe8 -'', # 0xe9 -'', # 0xea -'', # 0xeb -'', # 0xec -'', # 0xed -'', # 0xee -'', # 0xef -'', # 0xf0 -'', # 0xf1 -'', # 0xf2 -'', # 0xf3 -'', # 0xf4 -'', # 0xf5 -'', # 0xf6 -'', # 0xf7 -'', # 0xf8 -'', # 0xf9 -'', # 0xfa -'', # 0xfb -'', # 0xfc -'', # 0xfd -'', # 0xfe -'', # 0xff +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe +None, # 0xff ) diff --git a/libs/common/unidecode/x1d7.py b/libs/common/unidecode/x1d7.py index 6943bf45..46242ecd 100644 --- a/libs/common/unidecode/x1d7.py +++ b/libs/common/unidecode/x1d7.py @@ -1,210 +1,210 @@ data = ( -'', # 0x00 -'', # 0x01 -'', # 0x02 -'', # 0x03 -'', # 0x04 -'', # 0x05 -'', # 0x06 -'', # 0x07 -'', # 0x08 -'', # 0x09 -'', # 0x0a -'', # 0x0b -'', # 0x0c -'', # 0x0d -'', # 0x0e -'', # 0x0f -'', # 0x10 -'', # 0x11 -'', # 0x12 -'', # 0x13 -'', # 0x14 -'', # 0x15 -'', # 0x16 -'', # 0x17 -'', # 0x18 -'', # 0x19 -'', # 0x1a -'', # 0x1b -'', # 0x1c -'', # 0x1d -'', # 0x1e -'', # 0x1f -'', # 0x20 -'', # 0x21 -'', # 0x22 -'', # 0x23 -'', # 0x24 -'', # 0x25 -'', # 0x26 -'', # 0x27 -'', # 0x28 -'', # 0x29 -'', # 0x2a -'', # 0x2b -'', # 0x2c -'', # 0x2d -'', # 0x2e -'', # 0x2f -'', # 0x30 -'', # 0x31 -'', # 0x32 -'', # 0x33 -'', # 0x34 -'', # 0x35 -'', # 0x36 -'', # 0x37 -'', # 0x38 -'', # 0x39 -'', # 0x3a -'', # 0x3b -'', # 0x3c -'', # 0x3d -'', # 0x3e -'', # 0x3f -'', # 0x40 -'', # 0x41 -'', # 0x42 -'', # 0x43 -'', # 0x44 -'', # 0x45 -'', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 -'', # 0x4a -'', # 0x4b -'', # 0x4c -'', # 0x4d -'', # 0x4e -'', # 0x4f -'', # 0x50 -'', # 0x51 -'', # 0x52 -'', # 0x53 -'', # 0x54 -'', # 0x55 -'', # 0x56 -'', # 0x57 -'', # 0x58 -'', # 0x59 -'', # 0x5a -'', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'', # 0x5f -'', # 0x60 -'', # 0x61 -'', # 0x62 -'', # 0x63 -'', # 0x64 -'', # 0x65 -'', # 0x66 -'', # 0x67 -'', # 0x68 -'', # 0x69 -'', # 0x6a -'', # 0x6b -'', # 0x6c -'', # 0x6d -'', # 0x6e -'', # 0x6f -'', # 0x70 -'', # 0x71 -'', # 0x72 -'', # 0x73 -'', # 0x74 -'', # 0x75 -'', # 0x76 -'', # 0x77 -'', # 0x78 -'', # 0x79 -'', # 0x7a -'', # 0x7b -'', # 0x7c -'', # 0x7d -'', # 0x7e -'', # 0x7f -'', # 0x80 -'', # 0x81 -'', # 0x82 -'', # 0x83 -'', # 0x84 -'', # 0x85 -'', # 0x86 -'', # 0x87 -'', # 0x88 -'', # 0x89 -'', # 0x8a -'', # 0x8b -'', # 0x8c -'', # 0x8d -'', # 0x8e -'', # 0x8f -'', # 0x90 -'', # 0x91 -'', # 0x92 -'', # 0x93 -'', # 0x94 -'', # 0x95 -'', # 0x96 -'', # 0x97 -'', # 0x98 -'', # 0x99 -'', # 0x9a -'', # 0x9b -'', # 0x9c -'', # 0x9d -'', # 0x9e -'', # 0x9f -'', # 0xa0 -'', # 0xa1 -'', # 0xa2 -'', # 0xa3 -'', # 0xa4 -'', # 0xa5 -'', # 0xa6 -'', # 0xa7 -'', # 0xa8 -'', # 0xa9 -'', # 0xaa -'', # 0xab -'', # 0xac -'', # 0xad -'', # 0xae -'', # 0xaf -'', # 0xb0 -'', # 0xb1 -'', # 0xb2 -'', # 0xb3 -'', # 0xb4 -'', # 0xb5 -'', # 0xb6 -'', # 0xb7 -'', # 0xb8 -'', # 0xb9 -'', # 0xba -'', # 0xbb -'', # 0xbc -'', # 0xbd -'', # 0xbe -'', # 0xbf -'', # 0xc0 -'', # 0xc1 -'', # 0xc2 -'', # 0xc3 -'', # 0xc4 -'', # 0xc5 -'', # 0xc6 -'', # 0xc7 -'', # 0xc8 -'', # 0xc9 -'', # 0xca -'', # 0xcb -'', # 0xcc -'', # 0xcd +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +None, # 0x70 +None, # 0x71 +None, # 0x72 +None, # 0x73 +None, # 0x74 +None, # 0x75 +None, # 0x76 +None, # 0x77 +None, # 0x78 +None, # 0x79 +None, # 0x7a +None, # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd '0', # 0xce '1', # 0xcf '2', # 0xd0 diff --git a/libs/common/unidecode/x1f1.py b/libs/common/unidecode/x1f1.py index ba0481fc..2f78cf01 100644 --- a/libs/common/unidecode/x1f1.py +++ b/libs/common/unidecode/x1f1.py @@ -10,11 +10,11 @@ data = ( '7,', # 0x08 '8,', # 0x09 '9,', # 0x0a -'', # 0x0b -'', # 0x0c -'', # 0x0d -'', # 0x0e -'', # 0x0f +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f '(A)', # 0x10 '(B)', # 0x11 '(C)', # 0x12 @@ -41,218 +41,218 @@ data = ( '(X)', # 0x27 '(Y)', # 0x28 '(Z)', # 0x29 -'', # 0x2a -'', # 0x2b -'', # 0x2c -'', # 0x2d -'', # 0x2e -'', # 0x2f -'', # 0x30 -'', # 0x31 -'', # 0x32 -'', # 0x33 -'', # 0x34 -'', # 0x35 -'', # 0x36 -'', # 0x37 -'', # 0x38 -'', # 0x39 -'', # 0x3a -'', # 0x3b -'', # 0x3c -'', # 0x3d -'', # 0x3e -'', # 0x3f -'', # 0x40 -'', # 0x41 -'', # 0x42 -'', # 0x43 -'', # 0x44 -'', # 0x45 -'', # 0x46 -'', # 0x47 -'', # 0x48 -'', # 0x49 -'', # 0x4a -'', # 0x4b -'', # 0x4c -'', # 0x4d -'', # 0x4e -'', # 0x4f -'', # 0x50 -'', # 0x51 -'', # 0x52 -'', # 0x53 -'', # 0x54 -'', # 0x55 -'', # 0x56 -'', # 0x57 -'', # 0x58 -'', # 0x59 -'', # 0x5a -'', # 0x5b -'', # 0x5c -'', # 0x5d -'', # 0x5e -'', # 0x5f -'', # 0x60 -'', # 0x61 -'', # 0x62 -'', # 0x63 -'', # 0x64 -'', # 0x65 -'', # 0x66 -'', # 0x67 -'', # 0x68 -'', # 0x69 -'', # 0x6a -'', # 0x6b -'', # 0x6c -'', # 0x6d -'', # 0x6e -'', # 0x6f -'', # 0x70 -'', # 0x71 -'', # 0x72 -'', # 0x73 -'', # 0x74 -'', # 0x75 -'', # 0x76 -'', # 0x77 -'', # 0x78 -'', # 0x79 -'', # 0x7a -'', # 0x7b -'', # 0x7c -'', # 0x7d -'', # 0x7e -'', # 0x7f -'', # 0x80 -'', # 0x81 -'', # 0x82 -'', # 0x83 -'', # 0x84 -'', # 0x85 -'', # 0x86 -'', # 0x87 -'', # 0x88 -'', # 0x89 -'', # 0x8a -'', # 0x8b -'', # 0x8c -'', # 0x8d -'', # 0x8e -'', # 0x8f -'', # 0x90 -'', # 0x91 -'', # 0x92 -'', # 0x93 -'', # 0x94 -'', # 0x95 -'', # 0x96 -'', # 0x97 -'', # 0x98 -'', # 0x99 -'', # 0x9a -'', # 0x9b -'', # 0x9c -'', # 0x9d -'', # 0x9e -'', # 0x9f -'', # 0xa0 -'', # 0xa1 -'', # 0xa2 -'', # 0xa3 -'', # 0xa4 -'', # 0xa5 -'', # 0xa6 -'', # 0xa7 -'', # 0xa8 -'', # 0xa9 -'', # 0xaa -'', # 0xab -'', # 0xac -'', # 0xad -'', # 0xae -'', # 0xaf -'', # 0xb0 -'', # 0xb1 -'', # 0xb2 -'', # 0xb3 -'', # 0xb4 -'', # 0xb5 -'', # 0xb6 -'', # 0xb7 -'', # 0xb8 -'', # 0xb9 -'', # 0xba -'', # 0xbb -'', # 0xbc -'', # 0xbd -'', # 0xbe -'', # 0xbf -'', # 0xc0 -'', # 0xc1 -'', # 0xc2 -'', # 0xc3 -'', # 0xc4 -'', # 0xc5 -'', # 0xc6 -'', # 0xc7 -'', # 0xc8 -'', # 0xc9 -'', # 0xca -'', # 0xcb -'', # 0xcc -'', # 0xcd -'', # 0xce -'', # 0xcf -'', # 0xd0 -'', # 0xd1 -'', # 0xd2 -'', # 0xd3 -'', # 0xd4 -'', # 0xd5 -'', # 0xd6 -'', # 0xd7 -'', # 0xd8 -'', # 0xd9 -'', # 0xda -'', # 0xdb -'', # 0xdc -'', # 0xdd -'', # 0xde -'', # 0xdf -'', # 0xe0 -'', # 0xe1 -'', # 0xe2 -'', # 0xe3 -'', # 0xe4 -'', # 0xe5 -'', # 0xe6 -'', # 0xe7 -'', # 0xe8 -'', # 0xe9 -'', # 0xea -'', # 0xeb -'', # 0xec -'', # 0xed -'', # 0xee -'', # 0xef -'', # 0xf0 -'', # 0xf1 -'', # 0xf2 -'', # 0xf3 -'', # 0xf4 -'', # 0xf5 -'', # 0xf6 -'', # 0xf7 -'', # 0xf8 -'', # 0xf9 -'', # 0xfa -'', # 0xfb -'', # 0xfc -'', # 0xfd -'', # 0xfe -'', # 0xff +'', # 0x2a +'(C)', # 0x2b +'(R)', # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +'[A]', # 0x30 +'[B]', # 0x31 +'[C]', # 0x32 +'[D]', # 0x33 +'[E]', # 0x34 +'[F]', # 0x35 +'[G]', # 0x36 +'[H]', # 0x37 +'[I]', # 0x38 +'[J]', # 0x39 +'[K]', # 0x3a +'[L]', # 0x3b +'[M]', # 0x3c +'[N]', # 0x3d +'[O]', # 0x3e +'[P]', # 0x3f +'[Q]', # 0x40 +'[R]', # 0x41 +'[S]', # 0x42 +'[T]', # 0x43 +'[U]', # 0x44 +'[V]', # 0x45 +'[W]', # 0x46 +'[X]', # 0x47 +'[Y]', # 0x48 +'[Z]', # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +'(A)', # 0x50 +'(B)', # 0x51 +'(C)', # 0x52 +'(D)', # 0x53 +'(E)', # 0x54 +'(F)', # 0x55 +'(G)', # 0x56 +'(H)', # 0x57 +'(I)', # 0x58 +'(J)', # 0x59 +'(K)', # 0x5a +'(L)', # 0x5b +'(M)', # 0x5c +'(N)', # 0x5d +'(O)', # 0x5e +'(P)', # 0x5f +'(Q)', # 0x60 +'(R)', # 0x61 +'(S)', # 0x62 +'(T)', # 0x63 +'(U)', # 0x64 +'(V)', # 0x65 +'(W)', # 0x66 +'(X)', # 0x67 +'(Y)', # 0x68 +'(Z)', # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +'[A]', # 0x70 +'[B]', # 0x71 +'[C]', # 0x72 +'[D]', # 0x73 +'[E]', # 0x74 +'[F]', # 0x75 +'[G]', # 0x76 +'[H]', # 0x77 +'[I]', # 0x78 +'[J]', # 0x79 +'[K]', # 0x7a +'[L]', # 0x7b +'[M]', # 0x7c +'[N]', # 0x7d +'[O]', # 0x7e +'[P]', # 0x7f +'[Q]', # 0x80 +'[R]', # 0x81 +'[S]', # 0x82 +'[T]', # 0x83 +'[U]', # 0x84 +'[V]', # 0x85 +'[W]', # 0x86 +'[X]', # 0x87 +'[Y]', # 0x88 +'[Z]', # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +'DJ', # 0x90 +'[CL]', # 0x91 +'[COOL]', # 0x92 +'[FREE]', # 0x93 +'[ID]', # 0x94 +'[NEW]', # 0x95 +'[NG]', # 0x96 +'[OK]', # 0x97 +'[SOS]', # 0x98 +'[UP!]', # 0x99 +'[VS]', # 0x9a +'[3D]', # 0x9b +'[2nc-Scr]', # 0x9c +'[2K]', # 0x9d +'[4K]', # 0x9e +'[8K]', # 0x9f +'[5.1]', # 0xa0 +'[7.1]', # 0xa1 +'[22.2]', # 0xa2 +'[60P]', # 0xa3 +'[120P]', # 0xa4 +'[d]', # 0xa5 +'[HC]', # 0xa6 +'[HDR]', # 0xa7 +'[Hi-res]', # 0xa8 +'[Loss-less]', # 0xa9 +'[SHV]', # 0xaa +'[UHD]', # 0xab +'[VOD]', # 0xac +'(m)', # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe +None, # 0xff ) diff --git a/libs/common/unidecode/x1f6.py b/libs/common/unidecode/x1f6.py new file mode 100644 index 00000000..47eeada4 --- /dev/null +++ b/libs/common/unidecode/x1f6.py @@ -0,0 +1,258 @@ +data = ( +None, # 0x00 +None, # 0x01 +None, # 0x02 +None, # 0x03 +None, # 0x04 +None, # 0x05 +None, # 0x06 +None, # 0x07 +None, # 0x08 +None, # 0x09 +None, # 0x0a +None, # 0x0b +None, # 0x0c +None, # 0x0d +None, # 0x0e +None, # 0x0f +None, # 0x10 +None, # 0x11 +None, # 0x12 +None, # 0x13 +None, # 0x14 +None, # 0x15 +None, # 0x16 +None, # 0x17 +None, # 0x18 +None, # 0x19 +None, # 0x1a +None, # 0x1b +None, # 0x1c +None, # 0x1d +None, # 0x1e +None, # 0x1f +None, # 0x20 +None, # 0x21 +None, # 0x22 +None, # 0x23 +None, # 0x24 +None, # 0x25 +None, # 0x26 +None, # 0x27 +None, # 0x28 +None, # 0x29 +None, # 0x2a +None, # 0x2b +None, # 0x2c +None, # 0x2d +None, # 0x2e +None, # 0x2f +None, # 0x30 +None, # 0x31 +None, # 0x32 +None, # 0x33 +None, # 0x34 +None, # 0x35 +None, # 0x36 +None, # 0x37 +None, # 0x38 +None, # 0x39 +None, # 0x3a +None, # 0x3b +None, # 0x3c +None, # 0x3d +None, # 0x3e +None, # 0x3f +None, # 0x40 +None, # 0x41 +None, # 0x42 +None, # 0x43 +None, # 0x44 +None, # 0x45 +None, # 0x46 +None, # 0x47 +None, # 0x48 +None, # 0x49 +None, # 0x4a +None, # 0x4b +None, # 0x4c +None, # 0x4d +None, # 0x4e +None, # 0x4f +None, # 0x50 +None, # 0x51 +None, # 0x52 +None, # 0x53 +None, # 0x54 +None, # 0x55 +None, # 0x56 +None, # 0x57 +None, # 0x58 +None, # 0x59 +None, # 0x5a +None, # 0x5b +None, # 0x5c +None, # 0x5d +None, # 0x5e +None, # 0x5f +None, # 0x60 +None, # 0x61 +None, # 0x62 +None, # 0x63 +None, # 0x64 +None, # 0x65 +None, # 0x66 +None, # 0x67 +None, # 0x68 +None, # 0x69 +None, # 0x6a +None, # 0x6b +None, # 0x6c +None, # 0x6d +None, # 0x6e +None, # 0x6f +'et', # 0x70 +'et', # 0x71 +'et', # 0x72 +'et', # 0x73 +'&', # 0x74 +'&', # 0x75 +'"', # 0x76 +'"', # 0x77 +',,', # 0x78 +'!?', # 0x79 +'!?', # 0x7a +'!?', # 0x7b +None, # 0x7c +None, # 0x7d +None, # 0x7e +None, # 0x7f +None, # 0x80 +None, # 0x81 +None, # 0x82 +None, # 0x83 +None, # 0x84 +None, # 0x85 +None, # 0x86 +None, # 0x87 +None, # 0x88 +None, # 0x89 +None, # 0x8a +None, # 0x8b +None, # 0x8c +None, # 0x8d +None, # 0x8e +None, # 0x8f +None, # 0x90 +None, # 0x91 +None, # 0x92 +None, # 0x93 +None, # 0x94 +None, # 0x95 +None, # 0x96 +None, # 0x97 +None, # 0x98 +None, # 0x99 +None, # 0x9a +None, # 0x9b +None, # 0x9c +None, # 0x9d +None, # 0x9e +None, # 0x9f +None, # 0xa0 +None, # 0xa1 +None, # 0xa2 +None, # 0xa3 +None, # 0xa4 +None, # 0xa5 +None, # 0xa6 +None, # 0xa7 +None, # 0xa8 +None, # 0xa9 +None, # 0xaa +None, # 0xab +None, # 0xac +None, # 0xad +None, # 0xae +None, # 0xaf +None, # 0xb0 +None, # 0xb1 +None, # 0xb2 +None, # 0xb3 +None, # 0xb4 +None, # 0xb5 +None, # 0xb6 +None, # 0xb7 +None, # 0xb8 +None, # 0xb9 +None, # 0xba +None, # 0xbb +None, # 0xbc +None, # 0xbd +None, # 0xbe +None, # 0xbf +None, # 0xc0 +None, # 0xc1 +None, # 0xc2 +None, # 0xc3 +None, # 0xc4 +None, # 0xc5 +None, # 0xc6 +None, # 0xc7 +None, # 0xc8 +None, # 0xc9 +None, # 0xca +None, # 0xcb +None, # 0xcc +None, # 0xcd +None, # 0xce +None, # 0xcf +None, # 0xd0 +None, # 0xd1 +None, # 0xd2 +None, # 0xd3 +None, # 0xd4 +None, # 0xd5 +None, # 0xd6 +None, # 0xd7 +None, # 0xd8 +None, # 0xd9 +None, # 0xda +None, # 0xdb +None, # 0xdc +None, # 0xdd +None, # 0xde +None, # 0xdf +None, # 0xe0 +None, # 0xe1 +None, # 0xe2 +None, # 0xe3 +None, # 0xe4 +None, # 0xe5 +None, # 0xe6 +None, # 0xe7 +None, # 0xe8 +None, # 0xe9 +None, # 0xea +None, # 0xeb +None, # 0xec +None, # 0xed +None, # 0xee +None, # 0xef +None, # 0xf0 +None, # 0xf1 +None, # 0xf2 +None, # 0xf3 +None, # 0xf4 +None, # 0xf5 +None, # 0xf6 +None, # 0xf7 +None, # 0xf8 +None, # 0xf9 +None, # 0xfa +None, # 0xfb +None, # 0xfc +None, # 0xfd +None, # 0xfe +None, # 0xff +) diff --git a/libs/common/yaml/__init__.py b/libs/common/yaml/__init__.py index 9e35fe29..465041dc 100644 --- a/libs/common/yaml/__init__.py +++ b/libs/common/yaml/__init__.py @@ -8,7 +8,7 @@ from .nodes import * from .loader import * from .dumper import * -__version__ = '3.13' +__version__ = '6.0' try: from .cyaml import * __with_libyaml__ = True @@ -17,6 +17,15 @@ except ImportError: import io +#------------------------------------------------------------------------------ +# XXX "Warnings control" is now deprecated. Leaving in the API function to not +# break code that uses it. +#------------------------------------------------------------------------------ +def warnings(settings=None): + if settings is None: + return {} + +#------------------------------------------------------------------------------ def scan(stream, Loader=Loader): """ Scan a YAML stream and produce scanning tokens. @@ -62,7 +71,7 @@ def compose_all(stream, Loader=Loader): finally: loader.dispose() -def load(stream, Loader=Loader): +def load(stream, Loader): """ Parse the first YAML document in a stream and produce the corresponding Python object. @@ -73,7 +82,7 @@ def load(stream, Loader=Loader): finally: loader.dispose() -def load_all(stream, Loader=Loader): +def load_all(stream, Loader): """ Parse all YAML documents in a stream and produce corresponding Python objects. @@ -85,11 +94,33 @@ def load_all(stream, Loader=Loader): finally: loader.dispose() +def full_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + Resolve all tags except those known to be + unsafe on untrusted input. + """ + return load(stream, FullLoader) + +def full_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + Resolve all tags except those known to be + unsafe on untrusted input. + """ + return load_all(stream, FullLoader) + def safe_load(stream): """ Parse the first YAML document in a stream and produce the corresponding Python object. - Resolve only basic YAML tags. + + Resolve only basic YAML tags. This is known + to be safe for untrusted input. """ return load(stream, SafeLoader) @@ -97,10 +128,32 @@ def safe_load_all(stream): """ Parse all YAML documents in a stream and produce corresponding Python objects. - Resolve only basic YAML tags. + + Resolve only basic YAML tags. This is known + to be safe for untrusted input. """ return load_all(stream, SafeLoader) +def unsafe_load(stream): + """ + Parse the first YAML document in a stream + and produce the corresponding Python object. + + Resolve all tags, even those known to be + unsafe on untrusted input. + """ + return load(stream, UnsafeLoader) + +def unsafe_load_all(stream): + """ + Parse all YAML documents in a stream + and produce corresponding Python objects. + + Resolve all tags, even those known to be + unsafe on untrusted input. + """ + return load_all(stream, UnsafeLoader) + def emit(events, stream=None, Dumper=Dumper, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None): @@ -160,11 +213,11 @@ def serialize(node, stream=None, Dumper=Dumper, **kwds): return serialize_all([node], stream, Dumper=Dumper, **kwds) def dump_all(documents, stream=None, Dumper=Dumper, - default_style=None, default_flow_style=None, + default_style=None, default_flow_style=False, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): + version=None, tags=None, sort_keys=True): """ Serialize a sequence of Python objects into a YAML stream. If stream is None, return the produced string instead. @@ -181,7 +234,7 @@ def dump_all(documents, stream=None, Dumper=Dumper, canonical=canonical, indent=indent, width=width, allow_unicode=allow_unicode, line_break=line_break, encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) + explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys) try: dumper.open() for data in documents: @@ -216,42 +269,62 @@ def safe_dump(data, stream=None, **kwds): return dump_all([data], stream, Dumper=SafeDumper, **kwds) def add_implicit_resolver(tag, regexp, first=None, - Loader=Loader, Dumper=Dumper): + Loader=None, Dumper=Dumper): """ Add an implicit scalar detector. If an implicit scalar value matches the given regexp, the corresponding tag is assigned to the scalar. first is a sequence of possible initial characters or None. """ - Loader.add_implicit_resolver(tag, regexp, first) + if Loader is None: + loader.Loader.add_implicit_resolver(tag, regexp, first) + loader.FullLoader.add_implicit_resolver(tag, regexp, first) + loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first) + else: + Loader.add_implicit_resolver(tag, regexp, first) Dumper.add_implicit_resolver(tag, regexp, first) -def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): +def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper): """ Add a path based resolver for the given tag. A path is a list of keys that forms a path to a node in the representation tree. Keys can be string values, integers, or None. """ - Loader.add_path_resolver(tag, path, kind) + if Loader is None: + loader.Loader.add_path_resolver(tag, path, kind) + loader.FullLoader.add_path_resolver(tag, path, kind) + loader.UnsafeLoader.add_path_resolver(tag, path, kind) + else: + Loader.add_path_resolver(tag, path, kind) Dumper.add_path_resolver(tag, path, kind) -def add_constructor(tag, constructor, Loader=Loader): +def add_constructor(tag, constructor, Loader=None): """ Add a constructor for the given tag. Constructor is a function that accepts a Loader instance and a node object and produces the corresponding Python object. """ - Loader.add_constructor(tag, constructor) + if Loader is None: + loader.Loader.add_constructor(tag, constructor) + loader.FullLoader.add_constructor(tag, constructor) + loader.UnsafeLoader.add_constructor(tag, constructor) + else: + Loader.add_constructor(tag, constructor) -def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): +def add_multi_constructor(tag_prefix, multi_constructor, Loader=None): """ Add a multi-constructor for the given tag prefix. Multi-constructor is called for a node if its tag starts with tag_prefix. Multi-constructor accepts a Loader instance, a tag suffix, and a node object and produces the corresponding Python object. """ - Loader.add_multi_constructor(tag_prefix, multi_constructor) + if Loader is None: + loader.Loader.add_multi_constructor(tag_prefix, multi_constructor) + loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor) + loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor) + else: + Loader.add_multi_constructor(tag_prefix, multi_constructor) def add_representer(data_type, representer, Dumper=Dumper): """ @@ -278,7 +351,12 @@ class YAMLObjectMetaclass(type): def __init__(cls, name, bases, kwds): super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: - cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) + if isinstance(cls.yaml_loader, list): + for loader in cls.yaml_loader: + loader.add_constructor(cls.yaml_tag, cls.from_yaml) + else: + cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) + cls.yaml_dumper.add_representer(cls, cls.to_yaml) class YAMLObject(metaclass=YAMLObjectMetaclass): @@ -289,7 +367,7 @@ class YAMLObject(metaclass=YAMLObjectMetaclass): __slots__ = () # no direct instantiation, so allow immutable subclasses - yaml_loader = Loader + yaml_loader = [Loader, FullLoader, UnsafeLoader] yaml_dumper = Dumper yaml_tag = None diff --git a/libs/common/yaml/_yaml.cp37-win_amd64.pyd b/libs/common/yaml/_yaml.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..52e813be3c82134d511fe7191f274c4ea0e00797 GIT binary patch literal 264192 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\'\"%@`': + if ch in '#,[]{}&*!|>\'\"%@`': flow_indicators = True block_indicators = True if ch in '?:': @@ -689,7 +689,7 @@ class Emitter: flow_indicators = True if followed_by_whitespace: block_indicators = True - if ch == '#' and preceeded_by_whitespace: + if ch == '#' and preceded_by_whitespace: flow_indicators = True block_indicators = True @@ -698,7 +698,8 @@ class Emitter: line_breaks = True if not (ch == '\n' or '\x20' <= ch <= '\x7E'): if (ch == '\x85' or '\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD') and ch != '\uFEFF': + or '\uE000' <= ch <= '\uFFFD' + or '\U00010000' <= ch < '\U0010ffff') and ch != '\uFEFF': unicode_characters = True if not self.allow_unicode: special_characters = True @@ -730,7 +731,7 @@ class Emitter: # Prepare for the next character. index += 1 - preceeded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') + preceded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') followed_by_whitespace = (index+1 >= len(scalar) or scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029') @@ -1134,4 +1135,3 @@ class Emitter: spaces = (ch == ' ') breaks = (ch in '\n\x85\u2028\u2029') end += 1 - diff --git a/libs/common/yaml/loader.py b/libs/common/yaml/loader.py index 08c8f01b..e90c1122 100644 --- a/libs/common/yaml/loader.py +++ b/libs/common/yaml/loader.py @@ -1,5 +1,5 @@ -__all__ = ['BaseLoader', 'SafeLoader', 'Loader'] +__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader'] from .reader import * from .scanner import * @@ -18,6 +18,16 @@ class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolve BaseConstructor.__init__(self) BaseResolver.__init__(self) +class FullLoader(Reader, Scanner, Parser, Composer, FullConstructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + FullConstructor.__init__(self) + Resolver.__init__(self) + class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): def __init__(self, stream): @@ -38,3 +48,16 @@ class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): Constructor.__init__(self) Resolver.__init__(self) +# UnsafeLoader is the same as Loader (which is and was always unsafe on +# untrusted input). Use of either Loader or UnsafeLoader should be rare, since +# FullLoad should be able to load almost all YAML safely. Loader is left intact +# to ensure backwards compatibility. +class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): + + def __init__(self, stream): + Reader.__init__(self, stream) + Scanner.__init__(self) + Parser.__init__(self) + Composer.__init__(self) + Constructor.__init__(self) + Resolver.__init__(self) diff --git a/libs/common/yaml/reader.py b/libs/common/yaml/reader.py index f70e920f..774b0219 100644 --- a/libs/common/yaml/reader.py +++ b/libs/common/yaml/reader.py @@ -134,7 +134,7 @@ class Reader(object): self.encoding = 'utf-8' self.update(1) - NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD]') + NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]') def check_printable(self, data): match = self.NON_PRINTABLE.search(data) if match: @@ -183,10 +183,3 @@ class Reader(object): self.stream_pointer += len(data) if not data: self.eof = True - -#try: -# import psyco -# psyco.bind(Reader) -#except ImportError: -# pass - diff --git a/libs/common/yaml/representer.py b/libs/common/yaml/representer.py index b9e65c51..808ca06d 100644 --- a/libs/common/yaml/representer.py +++ b/libs/common/yaml/representer.py @@ -5,7 +5,7 @@ __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', from .error import * from .nodes import * -import datetime, sys, copyreg, types, base64, collections +import datetime, copyreg, types, base64, collections class RepresenterError(YAMLError): pass @@ -15,8 +15,9 @@ class BaseRepresenter: yaml_representers = {} yaml_multi_representers = {} - def __init__(self, default_style=None, default_flow_style=None): + def __init__(self, default_style=None, default_flow_style=False, sort_keys=True): self.default_style = default_style + self.sort_keys = sort_keys self.default_flow_style = default_flow_style self.represented_objects = {} self.object_keeper = [] @@ -107,10 +108,11 @@ class BaseRepresenter: best_style = True if hasattr(mapping, 'items'): mapping = list(mapping.items()) - try: - mapping = sorted(mapping) - except TypeError: - pass + if self.sort_keys: + try: + mapping = sorted(mapping) + except TypeError: + pass for item_key, item_value in mapping: node_key = self.represent_data(item_key) node_value = self.represent_data(item_value) @@ -226,7 +228,7 @@ class SafeRepresenter(BaseRepresenter): return self.represent_mapping(tag, state, flow_style=flow_style) def represent_undefined(self, data): - raise RepresenterError("cannot represent an object: %s" % data) + raise RepresenterError("cannot represent an object", data) SafeRepresenter.add_representer(type(None), SafeRepresenter.represent_none) @@ -316,7 +318,7 @@ class Representer(SafeRepresenter): elif hasattr(data, '__reduce__'): reduce = data.__reduce__() else: - raise RepresenterError("cannot represent object: %r" % data) + raise RepresenterError("cannot represent an object", data) reduce = (list(reduce)+[None]*5)[:5] function, args, state, listitems, dictitems = reduce args = list(args) @@ -367,7 +369,7 @@ Representer.add_representer(complex, Representer.add_representer(tuple, Representer.represent_tuple) -Representer.add_representer(type, +Representer.add_multi_representer(type, Representer.represent_name) Representer.add_representer(collections.OrderedDict, diff --git a/libs/common/yaml/resolver.py b/libs/common/yaml/resolver.py index 02b82e73..3522bdaa 100644 --- a/libs/common/yaml/resolver.py +++ b/libs/common/yaml/resolver.py @@ -146,8 +146,8 @@ class BaseResolver: resolvers = self.yaml_implicit_resolvers.get('', []) else: resolvers = self.yaml_implicit_resolvers.get(value[0], []) - resolvers += self.yaml_implicit_resolvers.get(None, []) - for tag, regexp in resolvers: + wildcard_resolvers = self.yaml_implicit_resolvers.get(None, []) + for tag, regexp in resolvers + wildcard_resolvers: if regexp.match(value): return tag implicit = implicit[1] @@ -177,7 +177,7 @@ Resolver.add_implicit_resolver( Resolver.add_implicit_resolver( 'tag:yaml.org,2002:float', re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? - |\.[0-9_]+(?:[eE][-+][0-9]+)? + |\.[0-9][0-9_]*(?:[eE][-+][0-9]+)? |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* |[-+]?\.(?:inf|Inf|INF) |\.(?:nan|NaN|NAN))$''', re.X), diff --git a/libs/common/yaml/scanner.py b/libs/common/yaml/scanner.py index c8d127b8..de925b07 100644 --- a/libs/common/yaml/scanner.py +++ b/libs/common/yaml/scanner.py @@ -124,10 +124,13 @@ class Scanner: def peek_token(self): # Return the next token, but do not delete if from the queue. + # Return None if no more tokens. while self.need_more_tokens(): self.fetch_more_tokens() if self.tokens: return self.tokens[0] + else: + return None def get_token(self): # Return the next token. @@ -329,7 +332,7 @@ class Scanner: ## } #if self.flow_level and self.indent > column: # raise ScannerError(None, None, - # "invalid intendation or unclosed '[' or '{'", + # "invalid indentation or unclosed '[' or '{'", # self.get_mark()) # In the flow context, indentation is ignored. We make the scanner less @@ -367,7 +370,7 @@ class Scanner: def fetch_stream_end(self): - # Set the current intendation to -1. + # Set the current indentation to -1. self.unwind_indent(-1) # Reset simple keys. @@ -386,7 +389,7 @@ class Scanner: def fetch_directive(self): - # Set the current intendation to -1. + # Set the current indentation to -1. self.unwind_indent(-1) # Reset simple keys. @@ -404,7 +407,7 @@ class Scanner: def fetch_document_indicator(self, TokenClass): - # Set the current intendation to -1. + # Set the current indentation to -1. self.unwind_indent(-1) # Reset simple keys. Note that there could not be a block collection @@ -516,7 +519,7 @@ class Scanner: # Block context needs additional checks. if not self.flow_level: - # Are we allowed to start a key (not nessesary a simple)? + # Are we allowed to start a key (not necessary a simple)? if not self.allow_simple_key: raise ScannerError(None, None, "mapping keys are not allowed here", @@ -564,7 +567,7 @@ class Scanner: else: # Block context needs additional checks. - # (Do we really need them? They will be catched by the parser + # (Do we really need them? They will be caught by the parser # anyway.) if not self.flow_level: @@ -897,7 +900,7 @@ class Scanner: # The specification does not restrict characters for anchors and # aliases. This may lead to problems, for instance, the document: # [ *alias, value ] - # can be interpteted in two ways, as + # can be interpreted in two ways, as # [ "value" ] # and # [ *alias , "value" ] @@ -1166,6 +1169,7 @@ class Scanner: ' ': '\x20', '\"': '\"', '\\': '\\', + '/': '/', 'N': '\x85', '_': '\xA0', 'L': '\u2028', @@ -1207,7 +1211,7 @@ class Scanner: for k in range(length): if self.peek(k) not in '0123456789ABCDEFabcdef': raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexdecimal numbers, but found %r" % + "expected escape sequence of %d hexadecimal numbers, but found %r" % (length, self.peek(k)), self.get_mark()) code = int(self.prefix(length), 16) chunks.append(chr(code)) @@ -1266,7 +1270,7 @@ class Scanner: def scan_plain(self): # See the specification for details. # We add an additional restriction for the flow context: - # plain scalars in the flow context cannot contain ',', ':' and '?'. + # plain scalars in the flow context cannot contain ',' or '?'. # We also keep track of the `allow_simple_key` flag here. # Indentation rules are loosed for the flow context. chunks = [] @@ -1285,18 +1289,12 @@ class Scanner: while True: ch = self.peek(length) if ch in '\0 \t\r\n\x85\u2028\u2029' \ - or (not self.flow_level and ch == ':' and - self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029') \ - or (self.flow_level and ch in ',:?[]{}'): + or (ch == ':' and + self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029' + + (u',[]{}' if self.flow_level else u''))\ + or (self.flow_level and ch in ',?[]{}'): break length += 1 - # It's not clear what we should do with ':' in the flow context. - if (self.flow_level and ch == ':' - and self.peek(length+1) not in '\0 \t\r\n\x85\u2028\u2029,[]{}'): - self.forward(length) - raise ScannerError("while scanning a plain scalar", start_mark, - "found unexpected ':'", self.get_mark(), - "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.") if length == 0: break self.allow_simple_key = False @@ -1405,7 +1403,7 @@ class Scanner: for k in range(2): if self.peek(k) not in '0123456789ABCDEFabcdef': raise ScannerError("while scanning a %s" % name, start_mark, - "expected URI escape sequence of 2 hexdecimal numbers, but found %r" + "expected URI escape sequence of 2 hexadecimal numbers, but found %r" % self.peek(k), self.get_mark()) codes.append(int(self.prefix(2), 16)) self.forward(2) @@ -1435,10 +1433,3 @@ class Scanner: self.forward() return ch return '' - -#try: -# import psyco -# psyco.bind(Scanner) -#except ImportError: -# pass - From 501be2c479f6dcc120d8c0f6a925b9f88b47a145 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 19:16:35 -0500 Subject: [PATCH 31/47] Update vendored requests to 2.25.1 Updates certifi to 2021.5.30 Updates chardet to 4.0.0 Updates idna to 2.10 Updates urllib3 to 1.26.13 --- libs/common/bin/{beet.exe => chardetect.exe} | Bin 108369 -> 108383 bytes libs/common/bin/mid3cp.exe | Bin 108396 -> 0 bytes libs/common/bin/mid3iconv.exe | Bin 108399 -> 0 bytes libs/common/bin/mid3v2.exe | Bin 108396 -> 0 bytes libs/common/bin/moggsplit.exe | Bin 108399 -> 0 bytes libs/common/bin/mutagen-inspect.exe | Bin 108405 -> 0 bytes libs/common/bin/mutagen-pony.exe | Bin 108402 -> 0 bytes libs/common/bin/unidecode.exe | Bin 108375 -> 0 bytes libs/common/certifi/__init__.py | 4 +- libs/common/certifi/__main__.py | 14 +- libs/common/certifi/cacert.pem | 1687 +++-- libs/common/certifi/core.py | 54 +- libs/common/chardet/__init__.py | 48 +- libs/common/chardet/charsetgroupprober.py | 1 + libs/common/chardet/cli/chardetect.py | 7 +- libs/common/chardet/compat.py | 6 +- libs/common/chardet/langbulgarianmodel.py | 4862 +++++++++++++- libs/common/chardet/langcyrillicmodel.py | 333 - libs/common/chardet/langgreekmodel.py | 4607 ++++++++++++- libs/common/chardet/langhebrewmodel.py | 4571 ++++++++++++- libs/common/chardet/langhungarianmodel.py | 4859 +++++++++++++- libs/common/chardet/langrussianmodel.py | 5718 +++++++++++++++++ libs/common/chardet/langthaimodel.py | 4570 ++++++++++++- libs/common/chardet/langturkishmodel.py | 4562 ++++++++++++- libs/common/chardet/metadata/__init__.py | 0 libs/common/chardet/metadata/languages.py | 310 + libs/common/chardet/sbcharsetprober.py | 45 +- libs/common/chardet/sbcsgroupprober.py | 76 +- libs/common/chardet/universaldetector.py | 8 +- libs/common/chardet/version.py | 2 +- libs/common/idna/core.py | 6 +- libs/common/idna/idnadata.py | 155 +- libs/common/idna/package_data.py | 2 +- libs/common/idna/uts46data.py | 846 ++- libs/common/requests/__init__.py | 42 +- libs/common/requests/__version__.py | 8 +- libs/common/requests/api.py | 11 +- libs/common/requests/auth.py | 4 +- libs/common/requests/compat.py | 2 + libs/common/requests/exceptions.py | 9 +- libs/common/requests/models.py | 23 +- libs/common/requests/sessions.py | 51 +- libs/common/requests/status_codes.py | 15 +- libs/common/requests/structures.py | 4 +- libs/common/requests/utils.py | 27 +- libs/common/urllib3/__init__.py | 88 +- libs/common/urllib3/_collections.py | 46 +- libs/common/urllib3/_version.py | 2 + libs/common/urllib3/connection.py | 474 +- libs/common/urllib3/connectionpool.py | 626 +- .../urllib3/contrib/_appengine_environ.py | 26 +- .../contrib/_securetransport/bindings.py | 320 +- .../contrib/_securetransport/low_level.py | 127 +- libs/common/urllib3/contrib/appengine.py | 131 +- libs/common/urllib3/contrib/ntlmpool.py | 105 +- libs/common/urllib3/contrib/pyopenssl.py | 216 +- .../common/urllib3/contrib/securetransport.py | 287 +- libs/common/urllib3/contrib/socks.py | 144 +- libs/common/urllib3/exceptions.py | 173 +- libs/common/urllib3/fields.py | 166 +- libs/common/urllib3/filepost.py | 18 +- libs/common/urllib3/packages/__init__.py | 5 - .../urllib3/packages/backports/makefile.py | 10 +- libs/common/urllib3/packages/six.py | 390 +- .../packages/ssl_match_hostname/__init__.py | 19 - libs/common/urllib3/poolmanager.py | 291 +- libs/common/urllib3/request.py | 88 +- libs/common/urllib3/response.py | 376 +- libs/common/urllib3/util/__init__.py | 71 +- libs/common/urllib3/util/connection.py | 37 +- libs/common/urllib3/util/proxy.py | 57 + libs/common/urllib3/util/queue.py | 1 + libs/common/urllib3/util/request.py | 70 +- libs/common/urllib3/util/response.py | 40 +- libs/common/urllib3/util/retry.py | 337 +- libs/common/urllib3/util/ssl_.py | 384 +- .../ssl_match_hostname.py} | 69 +- libs/common/urllib3/util/ssltransport.py | 221 + libs/common/urllib3/util/timeout.py | 136 +- libs/common/urllib3/util/url.py | 379 +- libs/common/urllib3/util/wait.py | 8 +- 81 files changed, 38530 insertions(+), 4957 deletions(-) rename libs/common/bin/{beet.exe => chardetect.exe} (99%) delete mode 100644 libs/common/bin/mid3cp.exe delete mode 100644 libs/common/bin/mid3iconv.exe delete mode 100644 libs/common/bin/mid3v2.exe delete mode 100644 libs/common/bin/moggsplit.exe delete mode 100644 libs/common/bin/mutagen-inspect.exe delete mode 100644 libs/common/bin/mutagen-pony.exe delete mode 100644 libs/common/bin/unidecode.exe delete mode 100644 libs/common/chardet/langcyrillicmodel.py create mode 100644 libs/common/chardet/langrussianmodel.py create mode 100644 libs/common/chardet/metadata/__init__.py create mode 100644 libs/common/chardet/metadata/languages.py create mode 100644 libs/common/urllib3/_version.py delete mode 100644 libs/common/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 libs/common/urllib3/util/proxy.py rename libs/common/urllib3/{packages/ssl_match_hostname/_implementation.py => util/ssl_match_hostname.py} (73%) create mode 100644 libs/common/urllib3/util/ssltransport.py diff --git a/libs/common/bin/beet.exe b/libs/common/bin/chardetect.exe similarity index 99% rename from libs/common/bin/beet.exe rename to libs/common/bin/chardetect.exe index 742e1c19ef89536fbe6732bc8353ecc3f8cf05fe..193b49855af679b788efea9ecd2fc5e4900ccccb 100644 GIT binary patch delta 84 zcmcb3j_v+AwuUW?>dSb)&a4S7@pasEih+UQ)O3?&j4ooy8Hq(HsU>>JIhlG;c4~6T W^oC`OeQ3(1mopl$eFo}eU;qHd79ZpQ delta 70 zcmcbAj_u+(wuUW?>dSa#C)9*i2)z8Uhk=1%&vcVzj4m8Wsi`H!dZn4u`<5~Gp()T@ N&S=2)6sVSg0RZ_r8SMZ7 diff --git a/libs/common/bin/mid3cp.exe b/libs/common/bin/mid3cp.exe deleted file mode 100644 index e3235f666537a7d7e5566c268855e0cd38250d01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108396 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 Kbz?1E;eP=1(=mbo diff --git a/libs/common/bin/mid3iconv.exe b/libs/common/bin/mid3iconv.exe deleted file mode 100644 index c67cd5b2ffcd56080b2d304930d4860d2db0fa65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108399 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 N7CL6#P@$Tx@IMx6GVK5W diff --git a/libs/common/bin/mid3v2.exe b/libs/common/bin/mid3v2.exe deleted file mode 100644 index a7c245b47ecf0b38a8d0c50a827a04ec7b86f0c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108396 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 Kbz?1E;eP;!Vlept diff --git a/libs/common/bin/moggsplit.exe b/libs/common/bin/moggsplit.exe deleted file mode 100644 index ecf55502106dffc9bea7444648e2d04713db9450..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108399 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 Owa_u^h6>emh5rH1Xf-JS diff --git a/libs/common/bin/mutagen-inspect.exe b/libs/common/bin/mutagen-inspect.exe deleted file mode 100644 index e41e38b85f71417c7f33c475e3815ed5e9be8387..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108405 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 Nh3;85RH>ya{11%xH$eaZ diff --git a/libs/common/bin/mutagen-pony.exe b/libs/common/bin/mutagen-pony.exe deleted file mode 100644 index efff7a2b6ca4ba719d2ce8511f3d663a5265514d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108402 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 diff --git a/libs/common/bin/unidecode.exe b/libs/common/bin/unidecode.exe deleted file mode 100644 index 7fd3c5dc46cc4032af72aaa40899ddb07a9540ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108375 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 detector.MINIMUM_THRESHOLD: + charset_name = prober.charset_name + lower_charset_name = prober.charset_name.lower() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if detector._has_win_bytes: + charset_name = detector.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + results.append({ + 'encoding': charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language, + }) + if len(results) > 0: + return sorted(results, key=lambda result: -result['confidence']) + + return [detector.result] diff --git a/libs/common/chardet/charsetgroupprober.py b/libs/common/chardet/charsetgroupprober.py index 8b3738ef..5812cef0 100644 --- a/libs/common/chardet/charsetgroupprober.py +++ b/libs/common/chardet/charsetgroupprober.py @@ -73,6 +73,7 @@ class CharSetGroupProber(CharSetProber): continue if state == ProbingState.FOUND_IT: self._best_guess_prober = prober + self._state = ProbingState.FOUND_IT return self.state elif state == ProbingState.NOT_ME: prober.active = False diff --git a/libs/common/chardet/cli/chardetect.py b/libs/common/chardet/cli/chardetect.py index f0a4cc5d..e1d8cd69 100644 --- a/libs/common/chardet/cli/chardetect.py +++ b/libs/common/chardet/cli/chardetect.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ Script which takes one or more file paths and reports on their detected encodings @@ -45,10 +44,10 @@ def description_of(lines, name='stdin'): if PY2: name = name.decode(sys.getfilesystemencoding(), 'ignore') if result['encoding']: - return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + return '{}: {} with confidence {}'.format(name, result['encoding'], result['confidence']) else: - return '{0}: no result'.format(name) + return '{}: no result'.format(name) def main(argv=None): @@ -69,7 +68,7 @@ def main(argv=None): type=argparse.FileType('rb'), nargs='*', default=[sys.stdin if PY2 else sys.stdin.buffer]) parser.add_argument('--version', action='version', - version='%(prog)s {0}'.format(__version__)) + version='%(prog)s {}'.format(__version__)) args = parser.parse_args(argv) for f in args.input: diff --git a/libs/common/chardet/compat.py b/libs/common/chardet/compat.py index ddd74687..8941572b 100644 --- a/libs/common/chardet/compat.py +++ b/libs/common/chardet/compat.py @@ -25,10 +25,12 @@ import sys if sys.version_info < (3, 0): PY2 = True PY3 = False - base_str = (str, unicode) + string_types = (str, unicode) text_type = unicode + iteritems = dict.iteritems else: PY2 = False PY3 = True - base_str = (bytes, str) + string_types = (bytes, str) text_type = str + iteritems = dict.items diff --git a/libs/common/chardet/langbulgarianmodel.py b/libs/common/chardet/langbulgarianmodel.py index 2aa4fb2e..561bfd90 100644 --- a/libs/common/chardet/langbulgarianmodel.py +++ b/libs/common/chardet/langbulgarianmodel.py @@ -1,228 +1,4650 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +BULGARIAN_LANG_MODEL = { + 63: { # 'e' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 45: { # '\xad' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 31: { # 'А' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 2, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 0, # 'и' + 26: 2, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 32: { # 'Б' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 2, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 35: { # 'В' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 2, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 43: { # 'Г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 37: { # 'Д' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 2, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 44: { # 'Е' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 2, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 0, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 55: { # 'Ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 47: { # 'З' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 40: { # 'И' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 2, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 3, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 59: { # 'Й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 33: { # 'К' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 46: { # 'Л' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 38: { # 'М' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 36: { # 'Н' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 41: { # 'О' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 30: { # 'П' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 2, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 39: { # 'Р' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 28: { # 'С' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 3, # 'А' + 32: 2, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 34: { # 'Т' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 51: { # 'У' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 48: { # 'Ф' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 49: { # 'Х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 53: { # 'Ц' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 50: { # 'Ч' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 54: { # 'Ш' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 57: { # 'Щ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 61: { # 'Ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 60: { # 'Ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 2, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 56: { # 'Я' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 1: { # 'а' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 18: { # 'б' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 3, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 9: { # 'в' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 20: { # 'г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 11: { # 'д' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 3: { # 'е' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 23: { # 'ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 15: { # 'з' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 2: { # 'и' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 26: { # 'й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 12: { # 'к' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 10: { # 'л' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 3, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 14: { # 'м' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 6: { # 'н' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 2, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 4: { # 'о' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 13: { # 'п' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 7: { # 'р' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 3, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 8: { # 'с' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 5: { # 'т' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 19: { # 'у' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 2, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 29: { # 'ф' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 2, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 25: { # 'х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 22: { # 'ц' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 21: { # 'ч' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 27: { # 'ш' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 24: { # 'щ' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 17: { # 'ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 52: { # 'ь' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 42: { # 'ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 16: { # 'я' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 1, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 3, # 'х' + 22: 2, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 58: { # 'є' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 62: { # '№' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -# this table is modified base on win1251BulgarianCharToOrderMap, so -# only number <64 is sure valid - -Latin5_BulgarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 -110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 -253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 -116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 -194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 -210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 - 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 - 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 - 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 - 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 - 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 - 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 -) - -win1251BulgarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 -110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 -253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 -116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 -206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 -221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 - 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 - 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 - 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 - 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 - 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 - 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 96.9392% -# first 1024 sequences:3.0618% -# rest sequences: 0.2992% -# negative sequences: 0.0020% -BulgarianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, -3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, -0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, -0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, -0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, -0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, -0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, -2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, -3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, -3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, -1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, -3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, -1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, -2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, -2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, -3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, -1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, -2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, -2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, -3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, -1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, -2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, -2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, -2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, -1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, -2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, -1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, -3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, -1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, -3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, -1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, -2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, -1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, -2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, -1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, -2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, -1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, -1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, -1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, -2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, -1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, -2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, -1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, -0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, -1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, -1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, -1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, -0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, -0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, -0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, -1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, -1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, -1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, -1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -) - -Latin5BulgarianModel = { - 'char_to_order_map': Latin5_BulgarianCharToOrderMap, - 'precedence_matrix': BulgarianLangModel, - 'typical_positive_ratio': 0.969392, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-5", - 'language': 'Bulgairan', +# Character Mapping Table(s): +ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 194, # '\x80' + 129: 195, # '\x81' + 130: 196, # '\x82' + 131: 197, # '\x83' + 132: 198, # '\x84' + 133: 199, # '\x85' + 134: 200, # '\x86' + 135: 201, # '\x87' + 136: 202, # '\x88' + 137: 203, # '\x89' + 138: 204, # '\x8a' + 139: 205, # '\x8b' + 140: 206, # '\x8c' + 141: 207, # '\x8d' + 142: 208, # '\x8e' + 143: 209, # '\x8f' + 144: 210, # '\x90' + 145: 211, # '\x91' + 146: 212, # '\x92' + 147: 213, # '\x93' + 148: 214, # '\x94' + 149: 215, # '\x95' + 150: 216, # '\x96' + 151: 217, # '\x97' + 152: 218, # '\x98' + 153: 219, # '\x99' + 154: 220, # '\x9a' + 155: 221, # '\x9b' + 156: 222, # '\x9c' + 157: 223, # '\x9d' + 158: 224, # '\x9e' + 159: 225, # '\x9f' + 160: 81, # '\xa0' + 161: 226, # 'Ё' + 162: 227, # 'Ђ' + 163: 228, # 'Ѓ' + 164: 229, # 'Є' + 165: 230, # 'Ѕ' + 166: 105, # 'І' + 167: 231, # 'Ї' + 168: 232, # 'Ј' + 169: 233, # 'Љ' + 170: 234, # 'Њ' + 171: 235, # 'Ћ' + 172: 236, # 'Ќ' + 173: 45, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 31, # 'А' + 177: 32, # 'Б' + 178: 35, # 'В' + 179: 43, # 'Г' + 180: 37, # 'Д' + 181: 44, # 'Е' + 182: 55, # 'Ж' + 183: 47, # 'З' + 184: 40, # 'И' + 185: 59, # 'Й' + 186: 33, # 'К' + 187: 46, # 'Л' + 188: 38, # 'М' + 189: 36, # 'Н' + 190: 41, # 'О' + 191: 30, # 'П' + 192: 39, # 'Р' + 193: 28, # 'С' + 194: 34, # 'Т' + 195: 51, # 'У' + 196: 48, # 'Ф' + 197: 49, # 'Х' + 198: 53, # 'Ц' + 199: 50, # 'Ч' + 200: 54, # 'Ш' + 201: 57, # 'Щ' + 202: 61, # 'Ъ' + 203: 239, # 'Ы' + 204: 67, # 'Ь' + 205: 240, # 'Э' + 206: 60, # 'Ю' + 207: 56, # 'Я' + 208: 1, # 'а' + 209: 18, # 'б' + 210: 9, # 'в' + 211: 20, # 'г' + 212: 11, # 'д' + 213: 3, # 'е' + 214: 23, # 'ж' + 215: 15, # 'з' + 216: 2, # 'и' + 217: 26, # 'й' + 218: 12, # 'к' + 219: 10, # 'л' + 220: 14, # 'м' + 221: 6, # 'н' + 222: 4, # 'о' + 223: 13, # 'п' + 224: 7, # 'р' + 225: 8, # 'с' + 226: 5, # 'т' + 227: 19, # 'у' + 228: 29, # 'ф' + 229: 25, # 'х' + 230: 22, # 'ц' + 231: 21, # 'ч' + 232: 27, # 'ш' + 233: 24, # 'щ' + 234: 17, # 'ъ' + 235: 75, # 'ы' + 236: 52, # 'ь' + 237: 241, # 'э' + 238: 42, # 'ю' + 239: 16, # 'я' + 240: 62, # '№' + 241: 242, # 'ё' + 242: 243, # 'ђ' + 243: 244, # 'ѓ' + 244: 58, # 'є' + 245: 245, # 'ѕ' + 246: 98, # 'і' + 247: 246, # 'ї' + 248: 247, # 'ј' + 249: 248, # 'љ' + 250: 249, # 'њ' + 251: 250, # 'ћ' + 252: 251, # 'ќ' + 253: 91, # '§' + 254: 252, # 'ў' + 255: 253, # 'џ' } -Win1251BulgarianModel = { - 'char_to_order_map': win1251BulgarianCharToOrderMap, - 'precedence_matrix': BulgarianLangModel, - 'typical_positive_ratio': 0.969392, - 'keep_english_letter': False, - 'charset_name': "windows-1251", - 'language': 'Bulgarian', +ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-5', + language='Bulgarian', + char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') + +WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 206, # 'Ђ' + 129: 207, # 'Ѓ' + 130: 208, # '‚' + 131: 209, # 'ѓ' + 132: 210, # '„' + 133: 211, # '…' + 134: 212, # '†' + 135: 213, # '‡' + 136: 120, # '€' + 137: 214, # '‰' + 138: 215, # 'Љ' + 139: 216, # '‹' + 140: 217, # 'Њ' + 141: 218, # 'Ќ' + 142: 219, # 'Ћ' + 143: 220, # 'Џ' + 144: 221, # 'ђ' + 145: 78, # '‘' + 146: 64, # '’' + 147: 83, # '“' + 148: 121, # '”' + 149: 98, # '•' + 150: 117, # '–' + 151: 105, # '—' + 152: 222, # None + 153: 223, # '™' + 154: 224, # 'љ' + 155: 225, # '›' + 156: 226, # 'њ' + 157: 227, # 'ќ' + 158: 228, # 'ћ' + 159: 229, # 'џ' + 160: 88, # '\xa0' + 161: 230, # 'Ў' + 162: 231, # 'ў' + 163: 232, # 'Ј' + 164: 233, # '¤' + 165: 122, # 'Ґ' + 166: 89, # '¦' + 167: 106, # '§' + 168: 234, # 'Ё' + 169: 235, # '©' + 170: 236, # 'Є' + 171: 237, # '«' + 172: 238, # '¬' + 173: 45, # '\xad' + 174: 239, # '®' + 175: 240, # 'Ї' + 176: 73, # '°' + 177: 80, # '±' + 178: 118, # 'І' + 179: 114, # 'і' + 180: 241, # 'ґ' + 181: 242, # 'µ' + 182: 243, # '¶' + 183: 244, # '·' + 184: 245, # 'ё' + 185: 62, # '№' + 186: 58, # 'є' + 187: 246, # '»' + 188: 247, # 'ј' + 189: 248, # 'Ѕ' + 190: 249, # 'ѕ' + 191: 250, # 'ї' + 192: 31, # 'А' + 193: 32, # 'Б' + 194: 35, # 'В' + 195: 43, # 'Г' + 196: 37, # 'Д' + 197: 44, # 'Е' + 198: 55, # 'Ж' + 199: 47, # 'З' + 200: 40, # 'И' + 201: 59, # 'Й' + 202: 33, # 'К' + 203: 46, # 'Л' + 204: 38, # 'М' + 205: 36, # 'Н' + 206: 41, # 'О' + 207: 30, # 'П' + 208: 39, # 'Р' + 209: 28, # 'С' + 210: 34, # 'Т' + 211: 51, # 'У' + 212: 48, # 'Ф' + 213: 49, # 'Х' + 214: 53, # 'Ц' + 215: 50, # 'Ч' + 216: 54, # 'Ш' + 217: 57, # 'Щ' + 218: 61, # 'Ъ' + 219: 251, # 'Ы' + 220: 67, # 'Ь' + 221: 252, # 'Э' + 222: 60, # 'Ю' + 223: 56, # 'Я' + 224: 1, # 'а' + 225: 18, # 'б' + 226: 9, # 'в' + 227: 20, # 'г' + 228: 11, # 'д' + 229: 3, # 'е' + 230: 23, # 'ж' + 231: 15, # 'з' + 232: 2, # 'и' + 233: 26, # 'й' + 234: 12, # 'к' + 235: 10, # 'л' + 236: 14, # 'м' + 237: 6, # 'н' + 238: 4, # 'о' + 239: 13, # 'п' + 240: 7, # 'р' + 241: 8, # 'с' + 242: 5, # 'т' + 243: 19, # 'у' + 244: 29, # 'ф' + 245: 25, # 'х' + 246: 22, # 'ц' + 247: 21, # 'ч' + 248: 27, # 'ш' + 249: 24, # 'щ' + 250: 17, # 'ъ' + 251: 75, # 'ы' + 252: 52, # 'ь' + 253: 253, # 'э' + 254: 42, # 'ю' + 255: 16, # 'я' } + +WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1251', + language='Bulgarian', + char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet='АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя') + diff --git a/libs/common/chardet/langcyrillicmodel.py b/libs/common/chardet/langcyrillicmodel.py deleted file mode 100644 index e5f9a1fd..00000000 --- a/libs/common/chardet/langcyrillicmodel.py +++ /dev/null @@ -1,333 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# KOI8-R language model -# Character Mapping Table: -KOI8R_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 -223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 -238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 - 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 - 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 - 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 - 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 -) - -win1251_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, -239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -) - -latin5_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, -) - -macCyrillic_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, -239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, -) - -IBM855_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 -191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, -206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, - 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, -220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, -230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, - 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, - 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, -250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, -) - -IBM866_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 -155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 -253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 - 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 - 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, - 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, - 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, -191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, -207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, -223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, - 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, -239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 97.6601% -# first 1024 sequences: 2.3389% -# rest sequences: 0.1237% -# negative sequences: 0.0009% -RussianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, -3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, -0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, -0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, -0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, -1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, -1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, -2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, -1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, -3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, -1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, -2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, -1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, -1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, -1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, -2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, -1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, -3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, -1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, -2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, -1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, -2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, -0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, -1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, -1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, -1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, -3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, -3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, -1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, -1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, -0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, -1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, -1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, -0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, -1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, -2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, -1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, -1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, -2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, -1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, -0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, -2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, -1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, -1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, -0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, -0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, -0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, -1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, -0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, -0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, -0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, -2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, -0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, -) - -Koi8rModel = { - 'char_to_order_map': KOI8R_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "KOI8-R", - 'language': 'Russian', -} - -Win1251CyrillicModel = { - 'char_to_order_map': win1251_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "windows-1251", - 'language': 'Russian', -} - -Latin5CyrillicModel = { - 'char_to_order_map': latin5_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-5", - 'language': 'Russian', -} - -MacCyrillicModel = { - 'char_to_order_map': macCyrillic_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "MacCyrillic", - 'language': 'Russian', -} - -Ibm866Model = { - 'char_to_order_map': IBM866_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "IBM866", - 'language': 'Russian', -} - -Ibm855Model = { - 'char_to_order_map': IBM855_char_to_order_map, - 'precedence_matrix': RussianLangModel, - 'typical_positive_ratio': 0.976601, - 'keep_english_letter': False, - 'charset_name': "IBM855", - 'language': 'Russian', -} diff --git a/libs/common/chardet/langgreekmodel.py b/libs/common/chardet/langgreekmodel.py index 53322216..02b94de6 100644 --- a/libs/common/chardet/langgreekmodel.py +++ b/libs/common/chardet/langgreekmodel.py @@ -1,225 +1,4398 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +GREEK_LANG_MODEL = { + 60: { # 'e' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 55: { # 'o' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 58: { # 't' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 1, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 36: { # '·' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 61: { # 'Ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 1, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 1, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 46: { # 'Έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 1, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 54: { # 'Ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 31: { # 'Α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 2, # 'Β' + 43: 2, # 'Γ' + 41: 1, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 51: { # 'Β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 1, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 43: { # 'Γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 1, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 41: { # 'Δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 34: { # 'Ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 0, # 'ώ' + }, + 40: { # 'Η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 52: { # 'Θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 1, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 47: { # 'Ι' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 1, # 'Β' + 43: 1, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 1, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 44: { # 'Κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 1, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 53: { # 'Λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 1, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 38: { # 'Μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 2, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 2, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 49: { # 'Ν' + 60: 2, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 59: { # 'Ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 39: { # 'Ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 1, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 2, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 35: { # 'Π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 1, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 48: { # 'Ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 37: { # 'Σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 2, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 33: { # 'Τ' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 2, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 45: { # 'Υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 2, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 1, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 56: { # 'Φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 1, # 'ώ' + }, + 50: { # 'Χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 1, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 57: { # 'Ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 17: { # 'ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 18: { # 'έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 22: { # 'ή' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 15: { # 'ί' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 1: { # 'α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 3, # 'ζ' + 13: 1, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 29: { # 'β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 20: { # 'γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 21: { # 'δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 3: { # 'ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 32: { # 'ζ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 13: { # 'η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 25: { # 'θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 5: { # 'ι' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 11: { # 'κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 16: { # 'λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 1, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 10: { # 'μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 3, # 'φ' + 23: 0, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 6: { # 'ν' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 30: { # 'ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 1, # 'ώ' + }, + 4: { # 'ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 9: { # 'π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 8: { # 'ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 14: { # 'ς' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 7: { # 'σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 2: { # 'τ' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 12: { # 'υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 28: { # 'φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 1, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 23: { # 'χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 42: { # 'ψ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 24: { # 'ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 1, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 19: { # 'ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 1, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 26: { # 'ύ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 27: { # 'ώ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 1, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 1, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin7_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 - 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 -253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 - 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 -253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 -253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 -110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 - 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 -124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 - 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 -) - -win1253_char_to_order_map = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 - 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 -253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 - 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 -253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 -253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 -110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 - 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 -124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 - 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 98.2851% -# first 1024 sequences:1.7001% -# rest sequences: 0.0359% -# negative sequences: 0.0148% -GreekLangModel = ( -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, -3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, -2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, -0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, -2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, -2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, -0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, -2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, -0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, -3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, -3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, -2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, -2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, -0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, -0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, -0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, -0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, -0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, -0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, -0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, -0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, -0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, -0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, -0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, -0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, -0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, -0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, -0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, -0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, -0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, -0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, -0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, -0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, -0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, -0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, -0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, -0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, -0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, -0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, -0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, -0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, -0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, -0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) - -Latin7GreekModel = { - 'char_to_order_map': Latin7_char_to_order_map, - 'precedence_matrix': GreekLangModel, - 'typical_positive_ratio': 0.982851, - 'keep_english_letter': False, - 'charset_name': "ISO-8859-7", - 'language': 'Greek', +# Character Mapping Table(s): +WINDOWS_1253_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '€' + 129: 255, # None + 130: 255, # '‚' + 131: 255, # 'ƒ' + 132: 255, # '„' + 133: 255, # '…' + 134: 255, # '†' + 135: 255, # '‡' + 136: 255, # None + 137: 255, # '‰' + 138: 255, # None + 139: 255, # '‹' + 140: 255, # None + 141: 255, # None + 142: 255, # None + 143: 255, # None + 144: 255, # None + 145: 255, # '‘' + 146: 255, # '’' + 147: 255, # '“' + 148: 255, # '”' + 149: 255, # '•' + 150: 255, # '–' + 151: 255, # '—' + 152: 255, # None + 153: 255, # '™' + 154: 255, # None + 155: 255, # '›' + 156: 255, # None + 157: 255, # None + 158: 255, # None + 159: 255, # None + 160: 253, # '\xa0' + 161: 233, # '΅' + 162: 61, # 'Ά' + 163: 253, # '£' + 164: 253, # '¤' + 165: 253, # '¥' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # None + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # '®' + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 253, # 'µ' + 182: 253, # '¶' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None } -Win1253GreekModel = { - 'char_to_order_map': win1253_char_to_order_map, - 'precedence_matrix': GreekLangModel, - 'typical_positive_ratio': 0.982851, - 'keep_english_letter': False, - 'charset_name': "windows-1253", - 'language': 'Greek', +WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel(charset_name='windows-1253', + language='Greek', + char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') + +ISO_8859_7_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '\x80' + 129: 255, # '\x81' + 130: 255, # '\x82' + 131: 255, # '\x83' + 132: 255, # '\x84' + 133: 255, # '\x85' + 134: 255, # '\x86' + 135: 255, # '\x87' + 136: 255, # '\x88' + 137: 255, # '\x89' + 138: 255, # '\x8a' + 139: 255, # '\x8b' + 140: 255, # '\x8c' + 141: 255, # '\x8d' + 142: 255, # '\x8e' + 143: 255, # '\x8f' + 144: 255, # '\x90' + 145: 255, # '\x91' + 146: 255, # '\x92' + 147: 255, # '\x93' + 148: 255, # '\x94' + 149: 255, # '\x95' + 150: 255, # '\x96' + 151: 255, # '\x97' + 152: 255, # '\x98' + 153: 255, # '\x99' + 154: 255, # '\x9a' + 155: 255, # '\x9b' + 156: 255, # '\x9c' + 157: 255, # '\x9d' + 158: 255, # '\x9e' + 159: 255, # '\x9f' + 160: 253, # '\xa0' + 161: 233, # '‘' + 162: 90, # '’' + 163: 253, # '£' + 164: 253, # '€' + 165: 253, # '₯' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # 'ͺ' + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # None + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 248, # '΅' + 182: 61, # 'Ά' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None } + +ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-7', + language='Greek', + char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet='ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ') + diff --git a/libs/common/chardet/langhebrewmodel.py b/libs/common/chardet/langhebrewmodel.py index 58f4c875..40fd674c 100644 --- a/libs/common/chardet/langhebrewmodel.py +++ b/libs/common/chardet/langhebrewmodel.py @@ -1,200 +1,4383 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Simon Montagu -# Portions created by the Initial Developer are Copyright (C) 2005 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# Shoshannah Forbes - original C code (?) -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HEBREW_LANG_MODEL = { + 50: { # 'a' + 50: 0, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 0, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 60: { # 'c' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 61: { # 'd' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 42: { # 'e' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 2, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 53: { # 'i' + 50: 1, # 'a' + 60: 2, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 0, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 56: { # 'l' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 2, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 54: { # 'n' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 49: { # 'o' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 51: { # 'r' + 50: 2, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 43: { # 's' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 2, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 44: { # 't' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 63: { # 'u' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 34: { # '\xa0' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 2, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 55: { # '´' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 48: { # '¼' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 39: { # '½' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 57: { # '¾' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 30: { # 'ְ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 59: { # 'ֱ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 41: { # 'ֲ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 33: { # 'ִ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 37: { # 'ֵ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 1, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 36: { # 'ֶ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 2, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 31: { # 'ַ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 29: { # 'ָ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 35: { # 'ֹ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 62: { # 'ֻ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 28: { # 'ּ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 3, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 3, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 2, # 'ׁ' + 45: 1, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 1, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 38: { # 'ׁ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 45: { # 'ׂ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 9: { # 'א' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 2, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 8: { # 'ב' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 1, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 20: { # 'ג' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 16: { # 'ד' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 3: { # 'ה' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 2: { # 'ו' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 3, # 'ֹ' + 62: 0, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 24: { # 'ז' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 1, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 14: { # 'ח' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 1, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 22: { # 'ט' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 1, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 2, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 1: { # 'י' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 25: { # 'ך' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 15: { # 'כ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 4: { # 'ל' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 11: { # 'ם' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 6: { # 'מ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 23: { # 'ן' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 12: { # 'נ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 19: { # 'ס' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 2, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 13: { # 'ע' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 1, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 26: { # 'ף' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 18: { # 'פ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 27: { # 'ץ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 21: { # 'צ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 1, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 0, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 17: { # 'ק' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 7: { # 'ר' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 10: { # 'ש' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 3, # 'ׁ' + 45: 2, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 5: { # 'ת' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 1, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 32: { # '–' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 52: { # '’' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 47: { # '“' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 46: { # '”' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 58: { # '†' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 2, # '†' + 40: 0, # '…' + }, + 40: { # '…' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Windows-1255 language model -# Character Mapping Table: -WIN1255_CHAR_TO_ORDER_MAP = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 - 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 -253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 - 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 -124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, -215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, - 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, -106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, - 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, -238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, - 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, - 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 98.4004% -# first 1024 sequences: 1.5981% -# rest sequences: 0.087% -# negative sequences: 0.0015% -HEBREW_LANG_MODEL = ( -0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, -3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, -1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, -1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, -1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, -1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, -1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, -0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, -0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, -1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, -0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, -0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, -0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, -0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, -0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, -0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, -0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, -0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, -0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, -3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, -0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, -0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, -0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, -1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, -0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, -3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, -0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, -0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, -0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, -0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, -2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, -0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, -0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, -0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, -1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, -0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, -2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, -1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, -2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, -2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, -0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, -0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, -) - -Win1255HebrewModel = { - 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, - 'precedence_matrix': HEBREW_LANG_MODEL, - 'typical_positive_ratio': 0.984004, - 'keep_english_letter': False, - 'charset_name': "windows-1255", - 'language': 'Hebrew', +# Character Mapping Table(s): +WINDOWS_1255_HEBREW_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 69, # 'A' + 66: 91, # 'B' + 67: 79, # 'C' + 68: 80, # 'D' + 69: 92, # 'E' + 70: 89, # 'F' + 71: 97, # 'G' + 72: 90, # 'H' + 73: 68, # 'I' + 74: 111, # 'J' + 75: 112, # 'K' + 76: 82, # 'L' + 77: 73, # 'M' + 78: 95, # 'N' + 79: 85, # 'O' + 80: 78, # 'P' + 81: 121, # 'Q' + 82: 86, # 'R' + 83: 71, # 'S' + 84: 67, # 'T' + 85: 102, # 'U' + 86: 107, # 'V' + 87: 84, # 'W' + 88: 114, # 'X' + 89: 103, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 50, # 'a' + 98: 74, # 'b' + 99: 60, # 'c' + 100: 61, # 'd' + 101: 42, # 'e' + 102: 76, # 'f' + 103: 70, # 'g' + 104: 64, # 'h' + 105: 53, # 'i' + 106: 105, # 'j' + 107: 93, # 'k' + 108: 56, # 'l' + 109: 65, # 'm' + 110: 54, # 'n' + 111: 49, # 'o' + 112: 66, # 'p' + 113: 110, # 'q' + 114: 51, # 'r' + 115: 43, # 's' + 116: 44, # 't' + 117: 63, # 'u' + 118: 81, # 'v' + 119: 77, # 'w' + 120: 98, # 'x' + 121: 75, # 'y' + 122: 108, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 124, # '€' + 129: 202, # None + 130: 203, # '‚' + 131: 204, # 'ƒ' + 132: 205, # '„' + 133: 40, # '…' + 134: 58, # '†' + 135: 206, # '‡' + 136: 207, # 'ˆ' + 137: 208, # '‰' + 138: 209, # None + 139: 210, # '‹' + 140: 211, # None + 141: 212, # None + 142: 213, # None + 143: 214, # None + 144: 215, # None + 145: 83, # '‘' + 146: 52, # '’' + 147: 47, # '“' + 148: 46, # '”' + 149: 72, # '•' + 150: 32, # '–' + 151: 94, # '—' + 152: 216, # '˜' + 153: 113, # '™' + 154: 217, # None + 155: 109, # '›' + 156: 218, # None + 157: 219, # None + 158: 220, # None + 159: 221, # None + 160: 34, # '\xa0' + 161: 116, # '¡' + 162: 222, # '¢' + 163: 118, # '£' + 164: 100, # '₪' + 165: 223, # '¥' + 166: 224, # '¦' + 167: 117, # '§' + 168: 119, # '¨' + 169: 104, # '©' + 170: 125, # '×' + 171: 225, # '«' + 172: 226, # '¬' + 173: 87, # '\xad' + 174: 99, # '®' + 175: 227, # '¯' + 176: 106, # '°' + 177: 122, # '±' + 178: 123, # '²' + 179: 228, # '³' + 180: 55, # '´' + 181: 229, # 'µ' + 182: 230, # '¶' + 183: 101, # '·' + 184: 231, # '¸' + 185: 232, # '¹' + 186: 120, # '÷' + 187: 233, # '»' + 188: 48, # '¼' + 189: 39, # '½' + 190: 57, # '¾' + 191: 234, # '¿' + 192: 30, # 'ְ' + 193: 59, # 'ֱ' + 194: 41, # 'ֲ' + 195: 88, # 'ֳ' + 196: 33, # 'ִ' + 197: 37, # 'ֵ' + 198: 36, # 'ֶ' + 199: 31, # 'ַ' + 200: 29, # 'ָ' + 201: 35, # 'ֹ' + 202: 235, # None + 203: 62, # 'ֻ' + 204: 28, # 'ּ' + 205: 236, # 'ֽ' + 206: 126, # '־' + 207: 237, # 'ֿ' + 208: 238, # '׀' + 209: 38, # 'ׁ' + 210: 45, # 'ׂ' + 211: 239, # '׃' + 212: 240, # 'װ' + 213: 241, # 'ױ' + 214: 242, # 'ײ' + 215: 243, # '׳' + 216: 127, # '״' + 217: 244, # None + 218: 245, # None + 219: 246, # None + 220: 247, # None + 221: 248, # None + 222: 249, # None + 223: 250, # None + 224: 9, # 'א' + 225: 8, # 'ב' + 226: 20, # 'ג' + 227: 16, # 'ד' + 228: 3, # 'ה' + 229: 2, # 'ו' + 230: 24, # 'ז' + 231: 14, # 'ח' + 232: 22, # 'ט' + 233: 1, # 'י' + 234: 25, # 'ך' + 235: 15, # 'כ' + 236: 4, # 'ל' + 237: 11, # 'ם' + 238: 6, # 'מ' + 239: 23, # 'ן' + 240: 12, # 'נ' + 241: 19, # 'ס' + 242: 13, # 'ע' + 243: 26, # 'ף' + 244: 18, # 'פ' + 245: 27, # 'ץ' + 246: 21, # 'צ' + 247: 17, # 'ק' + 248: 7, # 'ר' + 249: 10, # 'ש' + 250: 5, # 'ת' + 251: 251, # None + 252: 252, # None + 253: 128, # '\u200e' + 254: 96, # '\u200f' + 255: 253, # None } + +WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel(charset_name='windows-1255', + language='Hebrew', + char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, + language_model=HEBREW_LANG_MODEL, + typical_positive_ratio=0.984004, + keep_ascii_letters=False, + alphabet='אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ') + diff --git a/libs/common/chardet/langhungarianmodel.py b/libs/common/chardet/langhungarianmodel.py index bb7c095e..24a097f5 100644 --- a/libs/common/chardet/langhungarianmodel.py +++ b/libs/common/chardet/langhungarianmodel.py @@ -1,225 +1,4650 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HUNGARIAN_LANG_MODEL = { + 28: { # 'A' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 2, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 40: { # 'B' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 3, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 54: { # 'C' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 3, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 45: { # 'D' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 32: { # 'E' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 50: { # 'F' + 28: 1, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 0, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 49: { # 'G' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 38: { # 'H' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 39: { # 'I' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 53: { # 'J' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 36: { # 'K' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 2, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 41: { # 'L' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 34: { # 'M' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 35: { # 'N' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 47: { # 'O' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 46: { # 'P' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 43: { # 'R' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 2, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 2, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 33: { # 'S' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 3, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 37: { # 'T' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 57: { # 'U' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 48: { # 'V' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 55: { # 'Y' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 52: { # 'Z' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 2: { # 'a' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 18: { # 'b' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 26: { # 'c' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 17: { # 'd' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 1: { # 'e' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 2, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 27: { # 'f' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 3, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 12: { # 'g' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 20: { # 'h' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 9: { # 'i' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 1, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 22: { # 'j' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 7: { # 'k' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 3, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 6: { # 'l' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 3, # 'ő' + 56: 1, # 'ű' + }, + 13: { # 'm' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 4: { # 'n' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 1, # 'x' + 16: 3, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 8: { # 'o' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 23: { # 'p' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 10: { # 'r' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 2, # 'ű' + }, + 5: { # 's' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 3: { # 't' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 3, # 'ő' + 56: 2, # 'ű' + }, + 21: { # 'u' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 19: { # 'v' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 62: { # 'x' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 16: { # 'y' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 11: { # 'z' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 51: { # 'Á' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 44: { # 'É' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 61: { # 'Í' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 58: { # 'Ó' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 2, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 59: { # 'Ö' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 60: { # 'Ú' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 2, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 63: { # 'Ü' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 14: { # 'á' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 15: { # 'é' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 30: { # 'í' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 25: { # 'ó' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 24: { # 'ö' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 31: { # 'ú' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 3, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 29: { # 'ü' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 42: { # 'ő' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 56: { # 'ű' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin2_HungarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, - 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, -253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, - 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, -159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, -175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, -191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, - 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, -221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, -232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, - 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, -245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, -) - -win1250HungarianCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, - 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, -253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, - 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, -161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, -177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, -191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, - 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, -221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, -232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, - 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, -245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 94.7368% -# first 1024 sequences:5.2623% -# rest sequences: 0.8894% -# negative sequences: 0.0009% -HungarianLangModel = ( -0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, -3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, -3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, -3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, -0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, -0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, -3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, -0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, -0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, -3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, -1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, -1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, -1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, -3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, -2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, -2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, -2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, -2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, -2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, -3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, -2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, -2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, -2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, -1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, -1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, -3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, -1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, -1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, -2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, -2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, -2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, -3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, -2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, -1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, -1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, -2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, -2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, -1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, -1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, -2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, -1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, -1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, -2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, -2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, -2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, -1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, -1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, -1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, -0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, -2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, -2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, -1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, -2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, -1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, -1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, -2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, -2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, -2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, -1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, -2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, -0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, -0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, -2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -) - -Latin2HungarianModel = { - 'char_to_order_map': Latin2_HungarianCharToOrderMap, - 'precedence_matrix': HungarianLangModel, - 'typical_positive_ratio': 0.947368, - 'keep_english_letter': True, - 'charset_name': "ISO-8859-2", - 'language': 'Hungarian', +# Character Mapping Table(s): +WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 72, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 161, # '€' + 129: 162, # None + 130: 163, # '‚' + 131: 164, # None + 132: 165, # '„' + 133: 166, # '…' + 134: 167, # '†' + 135: 168, # '‡' + 136: 169, # None + 137: 170, # '‰' + 138: 171, # 'Š' + 139: 172, # '‹' + 140: 173, # 'Ś' + 141: 174, # 'Ť' + 142: 175, # 'Ž' + 143: 176, # 'Ź' + 144: 177, # None + 145: 178, # '‘' + 146: 179, # '’' + 147: 180, # '“' + 148: 78, # '”' + 149: 181, # '•' + 150: 69, # '–' + 151: 182, # '—' + 152: 183, # None + 153: 184, # '™' + 154: 185, # 'š' + 155: 186, # '›' + 156: 187, # 'ś' + 157: 188, # 'ť' + 158: 189, # 'ž' + 159: 190, # 'ź' + 160: 191, # '\xa0' + 161: 192, # 'ˇ' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ą' + 166: 197, # '¦' + 167: 76, # '§' + 168: 198, # '¨' + 169: 199, # '©' + 170: 200, # 'Ş' + 171: 201, # '«' + 172: 202, # '¬' + 173: 203, # '\xad' + 174: 204, # '®' + 175: 205, # 'Ż' + 176: 81, # '°' + 177: 206, # '±' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'µ' + 182: 211, # '¶' + 183: 212, # '·' + 184: 213, # '¸' + 185: 214, # 'ą' + 186: 215, # 'ş' + 187: 216, # '»' + 188: 217, # 'Ľ' + 189: 218, # '˝' + 190: 219, # 'ľ' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 83, # 'Â' + 195: 222, # 'Ă' + 196: 80, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 70, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 84, # 'ŕ' + 225: 14, # 'á' + 226: 75, # 'â' + 227: 242, # 'ă' + 228: 71, # 'ä' + 229: 82, # 'ĺ' + 230: 243, # 'ć' + 231: 73, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 85, # 'ę' + 235: 79, # 'ë' + 236: 86, # 'ě' + 237: 30, # 'í' + 238: 77, # 'î' + 239: 87, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 74, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' } -Win1250HungarianModel = { - 'char_to_order_map': win1250HungarianCharToOrderMap, - 'precedence_matrix': HungarianLangModel, - 'typical_positive_ratio': 0.947368, - 'keep_english_letter': True, - 'charset_name': "windows-1250", - 'language': 'Hungarian', +WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1250', + language='Hungarian', + char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') + +ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 71, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 159, # '\x80' + 129: 160, # '\x81' + 130: 161, # '\x82' + 131: 162, # '\x83' + 132: 163, # '\x84' + 133: 164, # '\x85' + 134: 165, # '\x86' + 135: 166, # '\x87' + 136: 167, # '\x88' + 137: 168, # '\x89' + 138: 169, # '\x8a' + 139: 170, # '\x8b' + 140: 171, # '\x8c' + 141: 172, # '\x8d' + 142: 173, # '\x8e' + 143: 174, # '\x8f' + 144: 175, # '\x90' + 145: 176, # '\x91' + 146: 177, # '\x92' + 147: 178, # '\x93' + 148: 179, # '\x94' + 149: 180, # '\x95' + 150: 181, # '\x96' + 151: 182, # '\x97' + 152: 183, # '\x98' + 153: 184, # '\x99' + 154: 185, # '\x9a' + 155: 186, # '\x9b' + 156: 187, # '\x9c' + 157: 188, # '\x9d' + 158: 189, # '\x9e' + 159: 190, # '\x9f' + 160: 191, # '\xa0' + 161: 192, # 'Ą' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ľ' + 166: 197, # 'Ś' + 167: 75, # '§' + 168: 198, # '¨' + 169: 199, # 'Š' + 170: 200, # 'Ş' + 171: 201, # 'Ť' + 172: 202, # 'Ź' + 173: 203, # '\xad' + 174: 204, # 'Ž' + 175: 205, # 'Ż' + 176: 79, # '°' + 177: 206, # 'ą' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'ľ' + 182: 211, # 'ś' + 183: 212, # 'ˇ' + 184: 213, # '¸' + 185: 214, # 'š' + 186: 215, # 'ş' + 187: 216, # 'ť' + 188: 217, # 'ź' + 189: 218, # '˝' + 190: 219, # 'ž' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 81, # 'Â' + 195: 222, # 'Ă' + 196: 78, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 69, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 82, # 'ŕ' + 225: 14, # 'á' + 226: 74, # 'â' + 227: 242, # 'ă' + 228: 70, # 'ä' + 229: 80, # 'ĺ' + 230: 243, # 'ć' + 231: 72, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 83, # 'ę' + 235: 77, # 'ë' + 236: 84, # 'ě' + 237: 30, # 'í' + 238: 76, # 'î' + 239: 85, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 73, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' } + +ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-2', + language='Hungarian', + char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű') + diff --git a/libs/common/chardet/langrussianmodel.py b/libs/common/chardet/langrussianmodel.py new file mode 100644 index 00000000..569689d0 --- /dev/null +++ b/libs/common/chardet/langrussianmodel.py @@ -0,0 +1,5718 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +RUSSIAN_LANG_MODEL = { + 37: { # 'А' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 44: { # 'Б' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 33: { # 'В' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 46: { # 'Г' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 41: { # 'Д' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 3, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 48: { # 'Е' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 56: { # 'Ж' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 0, # 'я' + }, + 51: { # 'З' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 42: { # 'И' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 60: { # 'Й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 36: { # 'К' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 49: { # 'Л' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 38: { # 'М' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 31: { # 'Н' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 34: { # 'О' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 2, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 2, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 35: { # 'П' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 1, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 45: { # 'Р' + 37: 2, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 32: { # 'С' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 2, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 40: { # 'Т' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 52: { # 'У' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 53: { # 'Ф' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 55: { # 'Х' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 58: { # 'Ц' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 50: { # 'Ч' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 57: { # 'Ш' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 63: { # 'Щ' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 62: { # 'Ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 61: { # 'Ь' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 47: { # 'Э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 59: { # 'Ю' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 43: { # 'Я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 3: { # 'а' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 21: { # 'б' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 10: { # 'в' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 19: { # 'г' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 13: { # 'д' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 2: { # 'е' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 24: { # 'ж' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 20: { # 'з' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 4: { # 'и' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 23: { # 'й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 11: { # 'к' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 8: { # 'л' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 12: { # 'м' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 5: { # 'н' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 1: { # 'о' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 15: { # 'п' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 9: { # 'р' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 7: { # 'с' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 6: { # 'т' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 14: { # 'у' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 2, # 'я' + }, + 39: { # 'ф' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 26: { # 'х' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 28: { # 'ц' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 22: { # 'ч' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 25: { # 'ш' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 29: { # 'щ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 2, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 54: { # 'ъ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 18: { # 'ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 1, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 17: { # 'ь' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 0, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 30: { # 'э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 27: { # 'ю' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 16: { # 'я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 2, # 'я' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +IBM866_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 3, # 'а' + 161: 21, # 'б' + 162: 10, # 'в' + 163: 19, # 'г' + 164: 13, # 'д' + 165: 2, # 'е' + 166: 24, # 'ж' + 167: 20, # 'з' + 168: 4, # 'и' + 169: 23, # 'й' + 170: 11, # 'к' + 171: 8, # 'л' + 172: 12, # 'м' + 173: 5, # 'н' + 174: 1, # 'о' + 175: 15, # 'п' + 176: 191, # '░' + 177: 192, # '▒' + 178: 193, # '▓' + 179: 194, # '│' + 180: 195, # '┤' + 181: 196, # '╡' + 182: 197, # '╢' + 183: 198, # '╖' + 184: 199, # '╕' + 185: 200, # '╣' + 186: 201, # '║' + 187: 202, # '╗' + 188: 203, # '╝' + 189: 204, # '╜' + 190: 205, # '╛' + 191: 206, # '┐' + 192: 207, # '└' + 193: 208, # '┴' + 194: 209, # '┬' + 195: 210, # '├' + 196: 211, # '─' + 197: 212, # '┼' + 198: 213, # '╞' + 199: 214, # '╟' + 200: 215, # '╚' + 201: 216, # '╔' + 202: 217, # '╩' + 203: 218, # '╦' + 204: 219, # '╠' + 205: 220, # '═' + 206: 221, # '╬' + 207: 222, # '╧' + 208: 223, # '╨' + 209: 224, # '╤' + 210: 225, # '╥' + 211: 226, # '╙' + 212: 227, # '╘' + 213: 228, # '╒' + 214: 229, # '╓' + 215: 230, # '╫' + 216: 231, # '╪' + 217: 232, # '┘' + 218: 233, # '┌' + 219: 234, # '█' + 220: 235, # '▄' + 221: 236, # '▌' + 222: 237, # '▐' + 223: 238, # '▀' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # 'Ё' + 241: 68, # 'ё' + 242: 240, # 'Є' + 243: 241, # 'є' + 244: 242, # 'Ї' + 245: 243, # 'ї' + 246: 244, # 'Ў' + 247: 245, # 'ў' + 248: 246, # '°' + 249: 247, # '∙' + 250: 248, # '·' + 251: 249, # '√' + 252: 250, # '№' + 253: 251, # '¤' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM866_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='IBM866', + language='Russian', + char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + +WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'Ђ' + 129: 192, # 'Ѓ' + 130: 193, # '‚' + 131: 194, # 'ѓ' + 132: 195, # '„' + 133: 196, # '…' + 134: 197, # '†' + 135: 198, # '‡' + 136: 199, # '€' + 137: 200, # '‰' + 138: 201, # 'Љ' + 139: 202, # '‹' + 140: 203, # 'Њ' + 141: 204, # 'Ќ' + 142: 205, # 'Ћ' + 143: 206, # 'Џ' + 144: 207, # 'ђ' + 145: 208, # '‘' + 146: 209, # '’' + 147: 210, # '“' + 148: 211, # '”' + 149: 212, # '•' + 150: 213, # '–' + 151: 214, # '—' + 152: 215, # None + 153: 216, # '™' + 154: 217, # 'љ' + 155: 218, # '›' + 156: 219, # 'њ' + 157: 220, # 'ќ' + 158: 221, # 'ћ' + 159: 222, # 'џ' + 160: 223, # '\xa0' + 161: 224, # 'Ў' + 162: 225, # 'ў' + 163: 226, # 'Ј' + 164: 227, # '¤' + 165: 228, # 'Ґ' + 166: 229, # '¦' + 167: 230, # '§' + 168: 231, # 'Ё' + 169: 232, # '©' + 170: 233, # 'Є' + 171: 234, # '«' + 172: 235, # '¬' + 173: 236, # '\xad' + 174: 237, # '®' + 175: 238, # 'Ї' + 176: 239, # '°' + 177: 240, # '±' + 178: 241, # 'І' + 179: 242, # 'і' + 180: 243, # 'ґ' + 181: 244, # 'µ' + 182: 245, # '¶' + 183: 246, # '·' + 184: 68, # 'ё' + 185: 247, # '№' + 186: 248, # 'є' + 187: 249, # '»' + 188: 250, # 'ј' + 189: 251, # 'Ѕ' + 190: 252, # 'ѕ' + 191: 253, # 'ї' + 192: 37, # 'А' + 193: 44, # 'Б' + 194: 33, # 'В' + 195: 46, # 'Г' + 196: 41, # 'Д' + 197: 48, # 'Е' + 198: 56, # 'Ж' + 199: 51, # 'З' + 200: 42, # 'И' + 201: 60, # 'Й' + 202: 36, # 'К' + 203: 49, # 'Л' + 204: 38, # 'М' + 205: 31, # 'Н' + 206: 34, # 'О' + 207: 35, # 'П' + 208: 45, # 'Р' + 209: 32, # 'С' + 210: 40, # 'Т' + 211: 52, # 'У' + 212: 53, # 'Ф' + 213: 55, # 'Х' + 214: 58, # 'Ц' + 215: 50, # 'Ч' + 216: 57, # 'Ш' + 217: 63, # 'Щ' + 218: 70, # 'Ъ' + 219: 62, # 'Ы' + 220: 61, # 'Ь' + 221: 47, # 'Э' + 222: 59, # 'Ю' + 223: 43, # 'Я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 16, # 'я' +} + +WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='windows-1251', + language='Russian', + char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + +IBM855_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'ђ' + 129: 192, # 'Ђ' + 130: 193, # 'ѓ' + 131: 194, # 'Ѓ' + 132: 68, # 'ё' + 133: 195, # 'Ё' + 134: 196, # 'є' + 135: 197, # 'Є' + 136: 198, # 'ѕ' + 137: 199, # 'Ѕ' + 138: 200, # 'і' + 139: 201, # 'І' + 140: 202, # 'ї' + 141: 203, # 'Ї' + 142: 204, # 'ј' + 143: 205, # 'Ј' + 144: 206, # 'љ' + 145: 207, # 'Љ' + 146: 208, # 'њ' + 147: 209, # 'Њ' + 148: 210, # 'ћ' + 149: 211, # 'Ћ' + 150: 212, # 'ќ' + 151: 213, # 'Ќ' + 152: 214, # 'ў' + 153: 215, # 'Ў' + 154: 216, # 'џ' + 155: 217, # 'Џ' + 156: 27, # 'ю' + 157: 59, # 'Ю' + 158: 54, # 'ъ' + 159: 70, # 'Ъ' + 160: 3, # 'а' + 161: 37, # 'А' + 162: 21, # 'б' + 163: 44, # 'Б' + 164: 28, # 'ц' + 165: 58, # 'Ц' + 166: 13, # 'д' + 167: 41, # 'Д' + 168: 2, # 'е' + 169: 48, # 'Е' + 170: 39, # 'ф' + 171: 53, # 'Ф' + 172: 19, # 'г' + 173: 46, # 'Г' + 174: 218, # '«' + 175: 219, # '»' + 176: 220, # '░' + 177: 221, # '▒' + 178: 222, # '▓' + 179: 223, # '│' + 180: 224, # '┤' + 181: 26, # 'х' + 182: 55, # 'Х' + 183: 4, # 'и' + 184: 42, # 'И' + 185: 225, # '╣' + 186: 226, # '║' + 187: 227, # '╗' + 188: 228, # '╝' + 189: 23, # 'й' + 190: 60, # 'Й' + 191: 229, # '┐' + 192: 230, # '└' + 193: 231, # '┴' + 194: 232, # '┬' + 195: 233, # '├' + 196: 234, # '─' + 197: 235, # '┼' + 198: 11, # 'к' + 199: 36, # 'К' + 200: 236, # '╚' + 201: 237, # '╔' + 202: 238, # '╩' + 203: 239, # '╦' + 204: 240, # '╠' + 205: 241, # '═' + 206: 242, # '╬' + 207: 243, # '¤' + 208: 8, # 'л' + 209: 49, # 'Л' + 210: 12, # 'м' + 211: 38, # 'М' + 212: 5, # 'н' + 213: 31, # 'Н' + 214: 1, # 'о' + 215: 34, # 'О' + 216: 15, # 'п' + 217: 244, # '┘' + 218: 245, # '┌' + 219: 246, # '█' + 220: 247, # '▄' + 221: 35, # 'П' + 222: 16, # 'я' + 223: 248, # '▀' + 224: 43, # 'Я' + 225: 9, # 'р' + 226: 45, # 'Р' + 227: 7, # 'с' + 228: 32, # 'С' + 229: 6, # 'т' + 230: 40, # 'Т' + 231: 14, # 'у' + 232: 52, # 'У' + 233: 24, # 'ж' + 234: 56, # 'Ж' + 235: 10, # 'в' + 236: 33, # 'В' + 237: 17, # 'ь' + 238: 61, # 'Ь' + 239: 249, # '№' + 240: 250, # '\xad' + 241: 18, # 'ы' + 242: 62, # 'Ы' + 243: 20, # 'з' + 244: 51, # 'З' + 245: 25, # 'ш' + 246: 57, # 'Ш' + 247: 30, # 'э' + 248: 47, # 'Э' + 249: 29, # 'щ' + 250: 63, # 'Щ' + 251: 22, # 'ч' + 252: 50, # 'Ч' + 253: 251, # '§' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM855_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='IBM855', + language='Russian', + char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + +KOI8_R_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '─' + 129: 192, # '│' + 130: 193, # '┌' + 131: 194, # '┐' + 132: 195, # '└' + 133: 196, # '┘' + 134: 197, # '├' + 135: 198, # '┤' + 136: 199, # '┬' + 137: 200, # '┴' + 138: 201, # '┼' + 139: 202, # '▀' + 140: 203, # '▄' + 141: 204, # '█' + 142: 205, # '▌' + 143: 206, # '▐' + 144: 207, # '░' + 145: 208, # '▒' + 146: 209, # '▓' + 147: 210, # '⌠' + 148: 211, # '■' + 149: 212, # '∙' + 150: 213, # '√' + 151: 214, # '≈' + 152: 215, # '≤' + 153: 216, # '≥' + 154: 217, # '\xa0' + 155: 218, # '⌡' + 156: 219, # '°' + 157: 220, # '²' + 158: 221, # '·' + 159: 222, # '÷' + 160: 223, # '═' + 161: 224, # '║' + 162: 225, # '╒' + 163: 68, # 'ё' + 164: 226, # '╓' + 165: 227, # '╔' + 166: 228, # '╕' + 167: 229, # '╖' + 168: 230, # '╗' + 169: 231, # '╘' + 170: 232, # '╙' + 171: 233, # '╚' + 172: 234, # '╛' + 173: 235, # '╜' + 174: 236, # '╝' + 175: 237, # '╞' + 176: 238, # '╟' + 177: 239, # '╠' + 178: 240, # '╡' + 179: 241, # 'Ё' + 180: 242, # '╢' + 181: 243, # '╣' + 182: 244, # '╤' + 183: 245, # '╥' + 184: 246, # '╦' + 185: 247, # '╧' + 186: 248, # '╨' + 187: 249, # '╩' + 188: 250, # '╪' + 189: 251, # '╫' + 190: 252, # '╬' + 191: 253, # '©' + 192: 27, # 'ю' + 193: 3, # 'а' + 194: 21, # 'б' + 195: 28, # 'ц' + 196: 13, # 'д' + 197: 2, # 'е' + 198: 39, # 'ф' + 199: 19, # 'г' + 200: 26, # 'х' + 201: 4, # 'и' + 202: 23, # 'й' + 203: 11, # 'к' + 204: 8, # 'л' + 205: 12, # 'м' + 206: 5, # 'н' + 207: 1, # 'о' + 208: 15, # 'п' + 209: 16, # 'я' + 210: 9, # 'р' + 211: 7, # 'с' + 212: 6, # 'т' + 213: 14, # 'у' + 214: 24, # 'ж' + 215: 10, # 'в' + 216: 17, # 'ь' + 217: 18, # 'ы' + 218: 20, # 'з' + 219: 25, # 'ш' + 220: 30, # 'э' + 221: 29, # 'щ' + 222: 22, # 'ч' + 223: 54, # 'ъ' + 224: 59, # 'Ю' + 225: 37, # 'А' + 226: 44, # 'Б' + 227: 58, # 'Ц' + 228: 41, # 'Д' + 229: 48, # 'Е' + 230: 53, # 'Ф' + 231: 46, # 'Г' + 232: 55, # 'Х' + 233: 42, # 'И' + 234: 60, # 'Й' + 235: 36, # 'К' + 236: 49, # 'Л' + 237: 38, # 'М' + 238: 31, # 'Н' + 239: 34, # 'О' + 240: 35, # 'П' + 241: 43, # 'Я' + 242: 45, # 'Р' + 243: 32, # 'С' + 244: 40, # 'Т' + 245: 52, # 'У' + 246: 56, # 'Ж' + 247: 33, # 'В' + 248: 61, # 'Ь' + 249: 62, # 'Ы' + 250: 51, # 'З' + 251: 57, # 'Ш' + 252: 47, # 'Э' + 253: 63, # 'Щ' + 254: 50, # 'Ч' + 255: 70, # 'Ъ' +} + +KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='KOI8-R', + language='Russian', + char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + +MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 191, # '†' + 161: 192, # '°' + 162: 193, # 'Ґ' + 163: 194, # '£' + 164: 195, # '§' + 165: 196, # '•' + 166: 197, # '¶' + 167: 198, # 'І' + 168: 199, # '®' + 169: 200, # '©' + 170: 201, # '™' + 171: 202, # 'Ђ' + 172: 203, # 'ђ' + 173: 204, # '≠' + 174: 205, # 'Ѓ' + 175: 206, # 'ѓ' + 176: 207, # '∞' + 177: 208, # '±' + 178: 209, # '≤' + 179: 210, # '≥' + 180: 211, # 'і' + 181: 212, # 'µ' + 182: 213, # 'ґ' + 183: 214, # 'Ј' + 184: 215, # 'Є' + 185: 216, # 'є' + 186: 217, # 'Ї' + 187: 218, # 'ї' + 188: 219, # 'Љ' + 189: 220, # 'љ' + 190: 221, # 'Њ' + 191: 222, # 'њ' + 192: 223, # 'ј' + 193: 224, # 'Ѕ' + 194: 225, # '¬' + 195: 226, # '√' + 196: 227, # 'ƒ' + 197: 228, # '≈' + 198: 229, # '∆' + 199: 230, # '«' + 200: 231, # '»' + 201: 232, # '…' + 202: 233, # '\xa0' + 203: 234, # 'Ћ' + 204: 235, # 'ћ' + 205: 236, # 'Ќ' + 206: 237, # 'ќ' + 207: 238, # 'ѕ' + 208: 239, # '–' + 209: 240, # '—' + 210: 241, # '“' + 211: 242, # '”' + 212: 243, # '‘' + 213: 244, # '’' + 214: 245, # '÷' + 215: 246, # '„' + 216: 247, # 'Ў' + 217: 248, # 'ў' + 218: 249, # 'Џ' + 219: 250, # 'џ' + 220: 251, # '№' + 221: 252, # 'Ё' + 222: 68, # 'ё' + 223: 16, # 'я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 255, # '€' +} + +MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='MacCyrillic', + language='Russian', + char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + +ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '\x80' + 129: 192, # '\x81' + 130: 193, # '\x82' + 131: 194, # '\x83' + 132: 195, # '\x84' + 133: 196, # '\x85' + 134: 197, # '\x86' + 135: 198, # '\x87' + 136: 199, # '\x88' + 137: 200, # '\x89' + 138: 201, # '\x8a' + 139: 202, # '\x8b' + 140: 203, # '\x8c' + 141: 204, # '\x8d' + 142: 205, # '\x8e' + 143: 206, # '\x8f' + 144: 207, # '\x90' + 145: 208, # '\x91' + 146: 209, # '\x92' + 147: 210, # '\x93' + 148: 211, # '\x94' + 149: 212, # '\x95' + 150: 213, # '\x96' + 151: 214, # '\x97' + 152: 215, # '\x98' + 153: 216, # '\x99' + 154: 217, # '\x9a' + 155: 218, # '\x9b' + 156: 219, # '\x9c' + 157: 220, # '\x9d' + 158: 221, # '\x9e' + 159: 222, # '\x9f' + 160: 223, # '\xa0' + 161: 224, # 'Ё' + 162: 225, # 'Ђ' + 163: 226, # 'Ѓ' + 164: 227, # 'Є' + 165: 228, # 'Ѕ' + 166: 229, # 'І' + 167: 230, # 'Ї' + 168: 231, # 'Ј' + 169: 232, # 'Љ' + 170: 233, # 'Њ' + 171: 234, # 'Ћ' + 172: 235, # 'Ќ' + 173: 236, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 37, # 'А' + 177: 44, # 'Б' + 178: 33, # 'В' + 179: 46, # 'Г' + 180: 41, # 'Д' + 181: 48, # 'Е' + 182: 56, # 'Ж' + 183: 51, # 'З' + 184: 42, # 'И' + 185: 60, # 'Й' + 186: 36, # 'К' + 187: 49, # 'Л' + 188: 38, # 'М' + 189: 31, # 'Н' + 190: 34, # 'О' + 191: 35, # 'П' + 192: 45, # 'Р' + 193: 32, # 'С' + 194: 40, # 'Т' + 195: 52, # 'У' + 196: 53, # 'Ф' + 197: 55, # 'Х' + 198: 58, # 'Ц' + 199: 50, # 'Ч' + 200: 57, # 'Ш' + 201: 63, # 'Щ' + 202: 70, # 'Ъ' + 203: 62, # 'Ы' + 204: 61, # 'Ь' + 205: 47, # 'Э' + 206: 59, # 'Ю' + 207: 43, # 'Я' + 208: 3, # 'а' + 209: 21, # 'б' + 210: 10, # 'в' + 211: 19, # 'г' + 212: 13, # 'д' + 213: 2, # 'е' + 214: 24, # 'ж' + 215: 20, # 'з' + 216: 4, # 'и' + 217: 23, # 'й' + 218: 11, # 'к' + 219: 8, # 'л' + 220: 12, # 'м' + 221: 5, # 'н' + 222: 1, # 'о' + 223: 15, # 'п' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # '№' + 241: 68, # 'ё' + 242: 240, # 'ђ' + 243: 241, # 'ѓ' + 244: 242, # 'є' + 245: 243, # 'ѕ' + 246: 244, # 'і' + 247: 245, # 'ї' + 248: 246, # 'ј' + 249: 247, # 'љ' + 250: 248, # 'њ' + 251: 249, # 'ћ' + 252: 250, # 'ќ' + 253: 251, # '§' + 254: 252, # 'ў' + 255: 255, # 'џ' +} + +ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-5', + language='Russian', + char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet='ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё') + diff --git a/libs/common/chardet/langthaimodel.py b/libs/common/chardet/langthaimodel.py index 15f94c2d..d0191f24 100644 --- a/libs/common/chardet/langthaimodel.py +++ b/libs/common/chardet/langthaimodel.py @@ -1,199 +1,4383 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### +#!/usr/bin/env python +# -*- coding: utf-8 -*- -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +THAI_LANG_MODEL = { + 5: { # 'ก' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 3, # 'ฎ' + 57: 2, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 1, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 30: { # 'ข' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 2, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 24: { # 'ค' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 3, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 8: { # 'ง' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 1, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 2, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 3, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 26: { # 'จ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 52: { # 'ฉ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 34: { # 'ช' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 1, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 51: { # 'ซ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 47: { # 'ญ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 58: { # 'ฎ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 57: { # 'ฏ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 49: { # 'ฐ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 53: { # 'ฑ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 55: { # 'ฒ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 43: { # 'ณ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 3, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 3, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 20: { # 'ด' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 2, # '็' + 6: 1, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 19: { # 'ต' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 2, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 44: { # 'ถ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 14: { # 'ท' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 3, # 'ศ' + 46: 1, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 48: { # 'ธ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 2, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 3: { # 'น' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 1, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 3, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 3, # 'โ' + 29: 3, # 'ใ' + 33: 3, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 17: { # 'บ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 25: { # 'ป' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 1, # 'ฎ' + 57: 3, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 1, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 2, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 39: { # 'ผ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 0, # 'ุ' + 35: 3, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 62: { # 'ฝ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 2, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 31: { # 'พ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 3, # 'ื' + 32: 1, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 0, # '่' + 7: 1, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 54: { # 'ฟ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 45: { # 'ภ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 9: { # 'ม' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 2, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 16: { # 'ย' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 2: { # 'ร' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 3, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 3, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 3, # 'เ' + 28: 3, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 61: { # 'ฤ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 2, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 15: { # 'ล' + 5: 2, # 'ก' + 30: 3, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 2, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 12: { # 'ว' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 42: { # 'ศ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 3, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 46: { # 'ษ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 2, # 'ฎ' + 57: 1, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 18: { # 'ส' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 3, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 21: { # 'ห' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 4: { # 'อ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 63: { # 'ฯ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 22: { # 'ะ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 10: { # 'ั' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 3, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 1: { # 'า' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 1, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 2, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 36: { # 'ำ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 23: { # 'ิ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 3, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 13: { # 'ี' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 40: { # 'ึ' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 27: { # 'ื' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 32: { # 'ุ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 1, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 35: { # 'ู' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 11: { # 'เ' + 5: 3, # 'ก' + 30: 3, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 3, # 'ฉ' + 34: 3, # 'ช' + 51: 2, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 3, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 28: { # 'แ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 3, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 41: { # 'โ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 29: { # 'ใ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 33: { # 'ไ' + 5: 1, # 'ก' + 30: 2, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 50: { # 'ๆ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 37: { # '็' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 6: { # '่' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 7: { # '้' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 38: { # '์' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 56: { # '๑' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 1, # '๕' + }, + 59: { # '๒' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 1, # '๑' + 59: 1, # '๒' + 60: 3, # '๕' + }, + 60: { # '๕' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 0, # '๕' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# The following result for thai was collected from a limited sample (1M). - -# Character Mapping Table: -TIS620CharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 -253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 -252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 -253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 -188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 -253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 - 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 -209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, -223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, -236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, - 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, - 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, - 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, - 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, - 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, -) - -# Model Table: -# total sequences: 100% -# first 512 sequences: 92.6386% -# first 1024 sequences:7.3177% -# rest sequences: 1.0230% -# negative sequences: 0.0436% -ThaiLangModel = ( -0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, -0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, -3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, -0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, -3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, -3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, -3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, -3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, -3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, -3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, -2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, -3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, -0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, -3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, -0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, -3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, -1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, -3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, -3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, -1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, -0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, -0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, -3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, -2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, -3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, -0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, -3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, -3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, -2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, -3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, -2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, -3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, -3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, -3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, -3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, -3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, -1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, -0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, -0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, -3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, -3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, -1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, -3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, -3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, -0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, -0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, -1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, -1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, -3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, -0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, -0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, -3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, -0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, -0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, -0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, -0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, -0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, -0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, -0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, -3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, -0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, -0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, -3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, -2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, -0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, -3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, -1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, -1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, -1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, -1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) - -TIS620ThaiModel = { - 'char_to_order_map': TIS620CharToOrderMap, - 'precedence_matrix': ThaiLangModel, - 'typical_positive_ratio': 0.926386, - 'keep_english_letter': False, - 'charset_name': "TIS-620", - 'language': 'Thai', +# Character Mapping Table(s): +TIS_620_THAI_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 182, # 'A' + 66: 106, # 'B' + 67: 107, # 'C' + 68: 100, # 'D' + 69: 183, # 'E' + 70: 184, # 'F' + 71: 185, # 'G' + 72: 101, # 'H' + 73: 94, # 'I' + 74: 186, # 'J' + 75: 187, # 'K' + 76: 108, # 'L' + 77: 109, # 'M' + 78: 110, # 'N' + 79: 111, # 'O' + 80: 188, # 'P' + 81: 189, # 'Q' + 82: 190, # 'R' + 83: 89, # 'S' + 84: 95, # 'T' + 85: 112, # 'U' + 86: 113, # 'V' + 87: 191, # 'W' + 88: 192, # 'X' + 89: 193, # 'Y' + 90: 194, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 64, # 'a' + 98: 72, # 'b' + 99: 73, # 'c' + 100: 114, # 'd' + 101: 74, # 'e' + 102: 115, # 'f' + 103: 116, # 'g' + 104: 102, # 'h' + 105: 81, # 'i' + 106: 201, # 'j' + 107: 117, # 'k' + 108: 90, # 'l' + 109: 103, # 'm' + 110: 78, # 'n' + 111: 82, # 'o' + 112: 96, # 'p' + 113: 202, # 'q' + 114: 91, # 'r' + 115: 79, # 's' + 116: 84, # 't' + 117: 104, # 'u' + 118: 105, # 'v' + 119: 97, # 'w' + 120: 98, # 'x' + 121: 92, # 'y' + 122: 203, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 209, # '\x80' + 129: 210, # '\x81' + 130: 211, # '\x82' + 131: 212, # '\x83' + 132: 213, # '\x84' + 133: 88, # '\x85' + 134: 214, # '\x86' + 135: 215, # '\x87' + 136: 216, # '\x88' + 137: 217, # '\x89' + 138: 218, # '\x8a' + 139: 219, # '\x8b' + 140: 220, # '\x8c' + 141: 118, # '\x8d' + 142: 221, # '\x8e' + 143: 222, # '\x8f' + 144: 223, # '\x90' + 145: 224, # '\x91' + 146: 99, # '\x92' + 147: 85, # '\x93' + 148: 83, # '\x94' + 149: 225, # '\x95' + 150: 226, # '\x96' + 151: 227, # '\x97' + 152: 228, # '\x98' + 153: 229, # '\x99' + 154: 230, # '\x9a' + 155: 231, # '\x9b' + 156: 232, # '\x9c' + 157: 233, # '\x9d' + 158: 234, # '\x9e' + 159: 235, # '\x9f' + 160: 236, # None + 161: 5, # 'ก' + 162: 30, # 'ข' + 163: 237, # 'ฃ' + 164: 24, # 'ค' + 165: 238, # 'ฅ' + 166: 75, # 'ฆ' + 167: 8, # 'ง' + 168: 26, # 'จ' + 169: 52, # 'ฉ' + 170: 34, # 'ช' + 171: 51, # 'ซ' + 172: 119, # 'ฌ' + 173: 47, # 'ญ' + 174: 58, # 'ฎ' + 175: 57, # 'ฏ' + 176: 49, # 'ฐ' + 177: 53, # 'ฑ' + 178: 55, # 'ฒ' + 179: 43, # 'ณ' + 180: 20, # 'ด' + 181: 19, # 'ต' + 182: 44, # 'ถ' + 183: 14, # 'ท' + 184: 48, # 'ธ' + 185: 3, # 'น' + 186: 17, # 'บ' + 187: 25, # 'ป' + 188: 39, # 'ผ' + 189: 62, # 'ฝ' + 190: 31, # 'พ' + 191: 54, # 'ฟ' + 192: 45, # 'ภ' + 193: 9, # 'ม' + 194: 16, # 'ย' + 195: 2, # 'ร' + 196: 61, # 'ฤ' + 197: 15, # 'ล' + 198: 239, # 'ฦ' + 199: 12, # 'ว' + 200: 42, # 'ศ' + 201: 46, # 'ษ' + 202: 18, # 'ส' + 203: 21, # 'ห' + 204: 76, # 'ฬ' + 205: 4, # 'อ' + 206: 66, # 'ฮ' + 207: 63, # 'ฯ' + 208: 22, # 'ะ' + 209: 10, # 'ั' + 210: 1, # 'า' + 211: 36, # 'ำ' + 212: 23, # 'ิ' + 213: 13, # 'ี' + 214: 40, # 'ึ' + 215: 27, # 'ื' + 216: 32, # 'ุ' + 217: 35, # 'ู' + 218: 86, # 'ฺ' + 219: 240, # None + 220: 241, # None + 221: 242, # None + 222: 243, # None + 223: 244, # '฿' + 224: 11, # 'เ' + 225: 28, # 'แ' + 226: 41, # 'โ' + 227: 29, # 'ใ' + 228: 33, # 'ไ' + 229: 245, # 'ๅ' + 230: 50, # 'ๆ' + 231: 37, # '็' + 232: 6, # '่' + 233: 7, # '้' + 234: 67, # '๊' + 235: 77, # '๋' + 236: 38, # '์' + 237: 93, # 'ํ' + 238: 246, # '๎' + 239: 247, # '๏' + 240: 68, # '๐' + 241: 56, # '๑' + 242: 59, # '๒' + 243: 65, # '๓' + 244: 69, # '๔' + 245: 60, # '๕' + 246: 70, # '๖' + 247: 80, # '๗' + 248: 71, # '๘' + 249: 87, # '๙' + 250: 248, # '๚' + 251: 249, # '๛' + 252: 250, # None + 253: 251, # None + 254: 252, # None + 255: 253, # None } + +TIS_620_THAI_MODEL = SingleByteCharSetModel(charset_name='TIS-620', + language='Thai', + char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, + language_model=THAI_LANG_MODEL, + typical_positive_ratio=0.926386, + keep_ascii_letters=False, + alphabet='กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛') + diff --git a/libs/common/chardet/langturkishmodel.py b/libs/common/chardet/langturkishmodel.py index a427a457..8ba93224 100644 --- a/libs/common/chardet/langturkishmodel.py +++ b/libs/common/chardet/langturkishmodel.py @@ -1,193 +1,4383 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Özgür Baskın - Turkish Language Model -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### -# 255: Control characters that usually does not exist in any text +from chardet.sbcharsetprober import SingleByteCharSetModel + + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +TURKISH_LANG_MODEL = { + 23: { # 'A' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 37: { # 'B' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 47: { # 'C' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 39: { # 'D' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 29: { # 'E' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 52: { # 'F' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 36: { # 'G' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 45: { # 'H' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 2, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 2, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 53: { # 'I' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 60: { # 'J' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 16: { # 'K' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 49: { # 'L' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 2, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 20: { # 'M' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 46: { # 'N' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 42: { # 'O' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 48: { # 'P' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 44: { # 'R' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 35: { # 'S' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 31: { # 'T' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 51: { # 'U' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 38: { # 'V' + 23: 1, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 62: { # 'W' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 43: { # 'Y' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 56: { # 'Z' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 1: { # 'a' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 21: { # 'b' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 28: { # 'c' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 3, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 1, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 12: { # 'd' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 2: { # 'e' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 18: { # 'f' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 1, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 27: { # 'g' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 25: { # 'h' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 3: { # 'i' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 24: { # 'j' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 10: { # 'k' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 5: { # 'l' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 13: { # 'm' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 4: { # 'n' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 15: { # 'o' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 2, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 2, # 'ş' + }, + 26: { # 'p' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 7: { # 'r' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 8: { # 's' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 9: { # 't' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 14: { # 'u' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 32: { # 'v' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 57: { # 'w' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 1, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 58: { # 'x' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 11: { # 'y' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 22: { # 'z' + 23: 2, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 2, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 3, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 2, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 63: { # '·' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 54: { # 'Ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 50: { # 'Ö' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 55: { # 'Ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 59: { # 'â' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 33: { # 'ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 61: { # 'î' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 34: { # 'ö' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 3, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 17: { # 'ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 30: { # 'ğ' + 23: 0, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 41: { # 'İ' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 6: { # 'ı' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 40: { # 'Ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 2, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 3, # 'f' + 27: 0, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 1, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 19: { # 'ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, +} + +# 255: Undefined characters that did not exist in training text # 254: Carriage/Return # 253: symbol (punctuation) that does not belong to word # 252: 0 - 9 +# 251: Control characters -# Character Mapping Table: -Latin5_TurkishCharToOrderMap = ( -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, - 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, -255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, - 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, -180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, -164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, -150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, - 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, -124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, - 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, - 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, - 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, -) - -TurkishLangModel = ( -3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, -3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, -3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, -3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, -3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, -3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, -3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, -2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, -3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, -2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, -1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, -3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, -3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, -2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, -3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, -2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, -3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, -3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, -3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, -0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, -3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, -0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, -3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, -1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, -3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, -2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, -2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, -3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, -0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, -1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, -3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, -1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, -3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, -0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, -3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, -0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, -1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, -1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, -2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, -2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, -0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, -2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, -3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, -2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, -0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, -3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, -1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, -1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, -0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, -3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, -0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, -3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, -1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, -3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, -1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, -2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, -0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, -3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, -0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, -0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, -3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, -0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, -0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, -3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, -0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, -3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, -0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, -0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, -3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, -0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, -3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, -0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, -3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, -0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, -0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, -0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, -3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, -0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, -3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, -0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, -0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, -0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, -2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, -1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, -3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, -0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, -0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, -3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, -0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, -2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, -2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, -0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, -1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, -0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, -2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, -0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -) - -Latin5TurkishModel = { - 'char_to_order_map': Latin5_TurkishCharToOrderMap, - 'precedence_matrix': TurkishLangModel, - 'typical_positive_ratio': 0.970290, - 'keep_english_letter': True, - 'charset_name': "ISO-8859-9", - 'language': 'Turkish', +# Character Mapping Table(s): +ISO_8859_9_TURKISH_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 255, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 255, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 255, # ' ' + 33: 255, # '!' + 34: 255, # '"' + 35: 255, # '#' + 36: 255, # '$' + 37: 255, # '%' + 38: 255, # '&' + 39: 255, # "'" + 40: 255, # '(' + 41: 255, # ')' + 42: 255, # '*' + 43: 255, # '+' + 44: 255, # ',' + 45: 255, # '-' + 46: 255, # '.' + 47: 255, # '/' + 48: 255, # '0' + 49: 255, # '1' + 50: 255, # '2' + 51: 255, # '3' + 52: 255, # '4' + 53: 255, # '5' + 54: 255, # '6' + 55: 255, # '7' + 56: 255, # '8' + 57: 255, # '9' + 58: 255, # ':' + 59: 255, # ';' + 60: 255, # '<' + 61: 255, # '=' + 62: 255, # '>' + 63: 255, # '?' + 64: 255, # '@' + 65: 23, # 'A' + 66: 37, # 'B' + 67: 47, # 'C' + 68: 39, # 'D' + 69: 29, # 'E' + 70: 52, # 'F' + 71: 36, # 'G' + 72: 45, # 'H' + 73: 53, # 'I' + 74: 60, # 'J' + 75: 16, # 'K' + 76: 49, # 'L' + 77: 20, # 'M' + 78: 46, # 'N' + 79: 42, # 'O' + 80: 48, # 'P' + 81: 69, # 'Q' + 82: 44, # 'R' + 83: 35, # 'S' + 84: 31, # 'T' + 85: 51, # 'U' + 86: 38, # 'V' + 87: 62, # 'W' + 88: 65, # 'X' + 89: 43, # 'Y' + 90: 56, # 'Z' + 91: 255, # '[' + 92: 255, # '\\' + 93: 255, # ']' + 94: 255, # '^' + 95: 255, # '_' + 96: 255, # '`' + 97: 1, # 'a' + 98: 21, # 'b' + 99: 28, # 'c' + 100: 12, # 'd' + 101: 2, # 'e' + 102: 18, # 'f' + 103: 27, # 'g' + 104: 25, # 'h' + 105: 3, # 'i' + 106: 24, # 'j' + 107: 10, # 'k' + 108: 5, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 15, # 'o' + 112: 26, # 'p' + 113: 64, # 'q' + 114: 7, # 'r' + 115: 8, # 's' + 116: 9, # 't' + 117: 14, # 'u' + 118: 32, # 'v' + 119: 57, # 'w' + 120: 58, # 'x' + 121: 11, # 'y' + 122: 22, # 'z' + 123: 255, # '{' + 124: 255, # '|' + 125: 255, # '}' + 126: 255, # '~' + 127: 255, # '\x7f' + 128: 180, # '\x80' + 129: 179, # '\x81' + 130: 178, # '\x82' + 131: 177, # '\x83' + 132: 176, # '\x84' + 133: 175, # '\x85' + 134: 174, # '\x86' + 135: 173, # '\x87' + 136: 172, # '\x88' + 137: 171, # '\x89' + 138: 170, # '\x8a' + 139: 169, # '\x8b' + 140: 168, # '\x8c' + 141: 167, # '\x8d' + 142: 166, # '\x8e' + 143: 165, # '\x8f' + 144: 164, # '\x90' + 145: 163, # '\x91' + 146: 162, # '\x92' + 147: 161, # '\x93' + 148: 160, # '\x94' + 149: 159, # '\x95' + 150: 101, # '\x96' + 151: 158, # '\x97' + 152: 157, # '\x98' + 153: 156, # '\x99' + 154: 155, # '\x9a' + 155: 154, # '\x9b' + 156: 153, # '\x9c' + 157: 152, # '\x9d' + 158: 151, # '\x9e' + 159: 106, # '\x9f' + 160: 150, # '\xa0' + 161: 149, # '¡' + 162: 148, # '¢' + 163: 147, # '£' + 164: 146, # '¤' + 165: 145, # '¥' + 166: 144, # '¦' + 167: 100, # '§' + 168: 143, # '¨' + 169: 142, # '©' + 170: 141, # 'ª' + 171: 140, # '«' + 172: 139, # '¬' + 173: 138, # '\xad' + 174: 137, # '®' + 175: 136, # '¯' + 176: 94, # '°' + 177: 80, # '±' + 178: 93, # '²' + 179: 135, # '³' + 180: 105, # '´' + 181: 134, # 'µ' + 182: 133, # '¶' + 183: 63, # '·' + 184: 132, # '¸' + 185: 131, # '¹' + 186: 130, # 'º' + 187: 129, # '»' + 188: 128, # '¼' + 189: 127, # '½' + 190: 126, # '¾' + 191: 125, # '¿' + 192: 124, # 'À' + 193: 104, # 'Á' + 194: 73, # 'Â' + 195: 99, # 'Ã' + 196: 79, # 'Ä' + 197: 85, # 'Å' + 198: 123, # 'Æ' + 199: 54, # 'Ç' + 200: 122, # 'È' + 201: 98, # 'É' + 202: 92, # 'Ê' + 203: 121, # 'Ë' + 204: 120, # 'Ì' + 205: 91, # 'Í' + 206: 103, # 'Î' + 207: 119, # 'Ï' + 208: 68, # 'Ğ' + 209: 118, # 'Ñ' + 210: 117, # 'Ò' + 211: 97, # 'Ó' + 212: 116, # 'Ô' + 213: 115, # 'Õ' + 214: 50, # 'Ö' + 215: 90, # '×' + 216: 114, # 'Ø' + 217: 113, # 'Ù' + 218: 112, # 'Ú' + 219: 111, # 'Û' + 220: 55, # 'Ü' + 221: 41, # 'İ' + 222: 40, # 'Ş' + 223: 86, # 'ß' + 224: 89, # 'à' + 225: 70, # 'á' + 226: 59, # 'â' + 227: 78, # 'ã' + 228: 71, # 'ä' + 229: 82, # 'å' + 230: 88, # 'æ' + 231: 33, # 'ç' + 232: 77, # 'è' + 233: 66, # 'é' + 234: 84, # 'ê' + 235: 83, # 'ë' + 236: 110, # 'ì' + 237: 75, # 'í' + 238: 61, # 'î' + 239: 96, # 'ï' + 240: 30, # 'ğ' + 241: 67, # 'ñ' + 242: 109, # 'ò' + 243: 74, # 'ó' + 244: 87, # 'ô' + 245: 102, # 'õ' + 246: 34, # 'ö' + 247: 95, # '÷' + 248: 81, # 'ø' + 249: 108, # 'ù' + 250: 76, # 'ú' + 251: 72, # 'û' + 252: 17, # 'ü' + 253: 6, # 'ı' + 254: 19, # 'ş' + 255: 107, # 'ÿ' } + +ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel(charset_name='ISO-8859-9', + language='Turkish', + char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, + language_model=TURKISH_LANG_MODEL, + typical_positive_ratio=0.97029, + keep_ascii_letters=True, + alphabet='ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş') + diff --git a/libs/common/chardet/metadata/__init__.py b/libs/common/chardet/metadata/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libs/common/chardet/metadata/languages.py b/libs/common/chardet/metadata/languages.py new file mode 100644 index 00000000..3237d5ab --- /dev/null +++ b/libs/common/chardet/metadata/languages.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Metadata about languages used by our model training code for our +SingleByteCharSetProbers. Could be used for other things in the future. + +This code is based on the language metadata from the uchardet project. +""" +from __future__ import absolute_import, print_function + +from string import ascii_letters + + +# TODO: Add Ukranian (KOI8-U) + +class Language(object): + """Metadata about a language useful for training models + + :ivar name: The human name for the language, in English. + :type name: str + :ivar iso_code: 2-letter ISO 639-1 if possible, 3-letter ISO code otherwise, + or use another catalog as a last resort. + :type iso_code: str + :ivar use_ascii: Whether or not ASCII letters should be included in trained + models. + :type use_ascii: bool + :ivar charsets: The charsets we want to support and create data for. + :type charsets: list of str + :ivar alphabet: The characters in the language's alphabet. If `use_ascii` is + `True`, you only need to add those not in the ASCII set. + :type alphabet: str + :ivar wiki_start_pages: The Wikipedia pages to start from if we're crawling + Wikipedia for training data. + :type wiki_start_pages: list of str + """ + def __init__(self, name=None, iso_code=None, use_ascii=True, charsets=None, + alphabet=None, wiki_start_pages=None): + super(Language, self).__init__() + self.name = name + self.iso_code = iso_code + self.use_ascii = use_ascii + self.charsets = charsets + if self.use_ascii: + if alphabet: + alphabet += ascii_letters + else: + alphabet = ascii_letters + elif not alphabet: + raise ValueError('Must supply alphabet if use_ascii is False') + self.alphabet = ''.join(sorted(set(alphabet))) if alphabet else None + self.wiki_start_pages = wiki_start_pages + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, + ', '.join('{}={!r}'.format(k, v) + for k, v in self.__dict__.items() + if not k.startswith('_'))) + + +LANGUAGES = {'Arabic': Language(name='Arabic', + iso_code='ar', + use_ascii=False, + # We only support encodings that use isolated + # forms, because the current recommendation is + # that the rendering system handles presentation + # forms. This means we purposefully skip IBM864. + charsets=['ISO-8859-6', 'WINDOWS-1256', + 'CP720', 'CP864'], + alphabet=u'ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ', + wiki_start_pages=[u'الصفحة_الرئيسية']), + 'Belarusian': Language(name='Belarusian', + iso_code='be', + use_ascii=False, + charsets=['ISO-8859-5', 'WINDOWS-1251', + 'IBM866', 'MacCyrillic'], + alphabet=(u'АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯ' + u'абвгдеёжзійклмнопрстуўфхцчшыьэюяʼ'), + wiki_start_pages=[u'Галоўная_старонка']), + 'Bulgarian': Language(name='Bulgarian', + iso_code='bg', + use_ascii=False, + charsets=['ISO-8859-5', 'WINDOWS-1251', + 'IBM855'], + alphabet=(u'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ' + u'абвгдежзийклмнопрстуфхцчшщъьюя'), + wiki_start_pages=[u'Начална_страница']), + 'Czech': Language(name='Czech', + iso_code='cz', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=u'áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ', + wiki_start_pages=[u'Hlavní_strana']), + 'Danish': Language(name='Danish', + iso_code='da', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'æøåÆØÅ', + wiki_start_pages=[u'Forside']), + 'German': Language(name='German', + iso_code='de', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + alphabet=u'äöüßÄÖÜ', + wiki_start_pages=[u'Wikipedia:Hauptseite']), + 'Greek': Language(name='Greek', + iso_code='el', + use_ascii=False, + charsets=['ISO-8859-7', 'WINDOWS-1253'], + alphabet=(u'αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώ' + u'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ'), + wiki_start_pages=[u'Πύλη:Κύρια']), + 'English': Language(name='English', + iso_code='en', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + wiki_start_pages=[u'Main_Page']), + 'Esperanto': Language(name='Esperanto', + iso_code='eo', + # Q, W, X, and Y not used at all + use_ascii=False, + charsets=['ISO-8859-3'], + alphabet=(u'abcĉdefgĝhĥijĵklmnoprsŝtuŭvz' + u'ABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ'), + wiki_start_pages=[u'Vikipedio:Ĉefpaĝo']), + 'Spanish': Language(name='Spanish', + iso_code='es', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'ñáéíóúüÑÁÉÍÓÚÜ', + wiki_start_pages=[u'Wikipedia:Portada']), + 'Estonian': Language(name='Estonian', + iso_code='et', + use_ascii=False, + charsets=['ISO-8859-4', 'ISO-8859-13', + 'WINDOWS-1257'], + # C, F, Š, Q, W, X, Y, Z, Ž are only for + # loanwords + alphabet=(u'ABDEGHIJKLMNOPRSTUVÕÄÖÜ' + u'abdeghijklmnoprstuvõäöü'), + wiki_start_pages=[u'Esileht']), + 'Finnish': Language(name='Finnish', + iso_code='fi', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'ÅÄÖŠŽåäöšž', + wiki_start_pages=[u'Wikipedia:Etusivu']), + 'French': Language(name='French', + iso_code='fr', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ', + wiki_start_pages=[u'Wikipédia:Accueil_principal', + u'Bœuf (animal)']), + 'Hebrew': Language(name='Hebrew', + iso_code='he', + use_ascii=False, + charsets=['ISO-8859-8', 'WINDOWS-1255'], + alphabet=u'אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ', + wiki_start_pages=[u'עמוד_ראשי']), + 'Croatian': Language(name='Croatian', + iso_code='hr', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=(u'abcčćdđefghijklmnoprsštuvzž' + u'ABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ'), + wiki_start_pages=[u'Glavna_stranica']), + 'Hungarian': Language(name='Hungarian', + iso_code='hu', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=(u'abcdefghijklmnoprstuvzáéíóöőúüű' + u'ABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ'), + wiki_start_pages=[u'Kezdőlap']), + 'Italian': Language(name='Italian', + iso_code='it', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'ÀÈÉÌÒÓÙàèéìòóù', + wiki_start_pages=[u'Pagina_principale']), + 'Lithuanian': Language(name='Lithuanian', + iso_code='lt', + use_ascii=False, + charsets=['ISO-8859-13', 'WINDOWS-1257', + 'ISO-8859-4'], + # Q, W, and X not used at all + alphabet=(u'AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ' + u'aąbcčdeęėfghiįyjklmnoprsštuųūvzž'), + wiki_start_pages=[u'Pagrindinis_puslapis']), + 'Latvian': Language(name='Latvian', + iso_code='lv', + use_ascii=False, + charsets=['ISO-8859-13', 'WINDOWS-1257', + 'ISO-8859-4'], + # Q, W, X, Y are only for loanwords + alphabet=(u'AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ' + u'aābcčdeēfgģhiījkķlļmnņoprsštuūvzž'), + wiki_start_pages=[u'Sākumlapa']), + 'Macedonian': Language(name='Macedonian', + iso_code='mk', + use_ascii=False, + charsets=['ISO-8859-5', 'WINDOWS-1251', + 'MacCyrillic', 'IBM855'], + alphabet=(u'АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШ' + u'абвгдѓежзѕијклљмнњопрстќуфхцчџш'), + wiki_start_pages=[u'Главна_страница']), + 'Dutch': Language(name='Dutch', + iso_code='nl', + use_ascii=True, + charsets=['ISO-8859-1', 'WINDOWS-1252'], + wiki_start_pages=[u'Hoofdpagina']), + 'Polish': Language(name='Polish', + iso_code='pl', + # Q and X are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=(u'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻ' + u'aąbcćdeęfghijklłmnńoóprsśtuwyzźż'), + wiki_start_pages=[u'Wikipedia:Strona_główna']), + 'Portuguese': Language(name='Portuguese', + iso_code='pt', + use_ascii=True, + charsets=['ISO-8859-1', 'ISO-8859-15', + 'WINDOWS-1252'], + alphabet=u'ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú', + wiki_start_pages=[u'Wikipédia:Página_principal']), + 'Romanian': Language(name='Romanian', + iso_code='ro', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=u'ăâîșțĂÂÎȘȚ', + wiki_start_pages=[u'Pagina_principală']), + 'Russian': Language(name='Russian', + iso_code='ru', + use_ascii=False, + charsets=['ISO-8859-5', 'WINDOWS-1251', + 'KOI8-R', 'MacCyrillic', 'IBM866', + 'IBM855'], + alphabet=(u'абвгдеёжзийклмнопрстуфхцчшщъыьэюя' + u'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'), + wiki_start_pages=[u'Заглавная_страница']), + 'Slovak': Language(name='Slovak', + iso_code='sk', + use_ascii=True, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=u'áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ', + wiki_start_pages=[u'Hlavná_stránka']), + 'Slovene': Language(name='Slovene', + iso_code='sl', + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=['ISO-8859-2', 'WINDOWS-1250'], + alphabet=(u'abcčdefghijklmnoprsštuvzž' + u'ABCČDEFGHIJKLMNOPRSŠTUVZŽ'), + wiki_start_pages=[u'Glavna_stran']), + # Serbian can be written in both Latin and Cyrillic, but there's no + # simple way to get the Latin alphabet pages from Wikipedia through + # the API, so for now we just support Cyrillic. + 'Serbian': Language(name='Serbian', + iso_code='sr', + alphabet=(u'АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШ' + u'абвгдђежзијклљмнњопрстћуфхцчџш'), + charsets=['ISO-8859-5', 'WINDOWS-1251', + 'MacCyrillic', 'IBM855'], + wiki_start_pages=[u'Главна_страна']), + 'Thai': Language(name='Thai', + iso_code='th', + use_ascii=False, + charsets=['ISO-8859-11', 'TIS-620', 'CP874'], + alphabet=u'กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛', + wiki_start_pages=[u'หน้าหลัก']), + 'Turkish': Language(name='Turkish', + iso_code='tr', + # Q, W, and X are not used by Turkish + use_ascii=False, + charsets=['ISO-8859-3', 'ISO-8859-9', + 'WINDOWS-1254'], + alphabet=(u'abcçdefgğhıijklmnoöprsştuüvyzâîû' + u'ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ'), + wiki_start_pages=[u'Ana_Sayfa']), + 'Vietnamese': Language(name='Vietnamese', + iso_code='vi', + use_ascii=False, + # Windows-1258 is the only common 8-bit + # Vietnamese encoding supported by Python. + # From Wikipedia: + # For systems that lack support for Unicode, + # dozens of 8-bit Vietnamese code pages are + # available.[1] The most common are VISCII + # (TCVN 5712:1993), VPS, and Windows-1258.[3] + # Where ASCII is required, such as when + # ensuring readability in plain text e-mail, + # Vietnamese letters are often encoded + # according to Vietnamese Quoted-Readable + # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4] + # though usage of either variable-width + # scheme has declined dramatically following + # the adoption of Unicode on the World Wide + # Web. + charsets=['WINDOWS-1258'], + alphabet=(u'aăâbcdđeêghiklmnoôơpqrstuưvxy' + u'AĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY'), + wiki_start_pages=[u'Chữ_Quốc_ngữ']), + } diff --git a/libs/common/chardet/sbcharsetprober.py b/libs/common/chardet/sbcharsetprober.py index 0adb51de..46ba835c 100644 --- a/libs/common/chardet/sbcharsetprober.py +++ b/libs/common/chardet/sbcharsetprober.py @@ -26,10 +26,22 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from collections import namedtuple + from .charsetprober import CharSetProber from .enums import CharacterCategory, ProbingState, SequenceLikelihood +SingleByteCharSetModel = namedtuple('SingleByteCharSetModel', + ['charset_name', + 'language', + 'char_to_order_map', + 'language_model', + 'typical_positive_ratio', + 'keep_ascii_letters', + 'alphabet']) + + class SingleByteCharSetProber(CharSetProber): SAMPLE_SIZE = 64 SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 @@ -65,25 +77,25 @@ class SingleByteCharSetProber(CharSetProber): if self._name_prober: return self._name_prober.charset_name else: - return self._model['charset_name'] + return self._model.charset_name @property def language(self): if self._name_prober: return self._name_prober.language else: - return self._model.get('language') + return self._model.language def feed(self, byte_str): - if not self._model['keep_english_letter']: + # TODO: Make filter_international_words keep things in self.alphabet + if not self._model.keep_ascii_letters: byte_str = self.filter_international_words(byte_str) if not byte_str: return self.state - char_to_order_map = self._model['char_to_order_map'] - for i, c in enumerate(byte_str): - # XXX: Order is in range 1-64, so one would think we want 0-63 here, - # but that leads to 27 more test failures than before. - order = char_to_order_map[c] + char_to_order_map = self._model.char_to_order_map + language_model = self._model.language_model + for char in byte_str: + order = char_to_order_map.get(char, CharacterCategory.UNDEFINED) # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but # CharacterCategory.SYMBOL is actually 253, so we use CONTROL # to make it closer to the original intent. The only difference @@ -91,20 +103,21 @@ class SingleByteCharSetProber(CharSetProber): # _total_char purposes. if order < CharacterCategory.CONTROL: self._total_char += 1 + # TODO: Follow uchardet's lead and discount confidence for frequent + # control characters. + # See https://github.com/BYVoid/uchardet/commit/55b4f23971db61 if order < self.SAMPLE_SIZE: self._freq_char += 1 if self._last_order < self.SAMPLE_SIZE: self._total_seqs += 1 if not self._reversed: - i = (self._last_order * self.SAMPLE_SIZE) + order - model = self._model['precedence_matrix'][i] - else: # reverse the order of the letters in the lookup - i = (order * self.SAMPLE_SIZE) + self._last_order - model = self._model['precedence_matrix'][i] - self._seq_counters[model] += 1 + lm_cat = language_model[self._last_order][order] + else: + lm_cat = language_model[order][self._last_order] + self._seq_counters[lm_cat] += 1 self._last_order = order - charset_name = self._model['charset_name'] + charset_name = self._model.charset_name if self.state == ProbingState.DETECTING: if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: confidence = self.get_confidence() @@ -125,7 +138,7 @@ class SingleByteCharSetProber(CharSetProber): r = 0.01 if self._total_seqs > 0: r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / - self._total_seqs / self._model['typical_positive_ratio']) + self._total_seqs / self._model.typical_positive_ratio) r = r * self._freq_char / self._total_char if r >= 1.0: r = 0.99 diff --git a/libs/common/chardet/sbcsgroupprober.py b/libs/common/chardet/sbcsgroupprober.py index 98e95dc1..bdeef4e1 100644 --- a/libs/common/chardet/sbcsgroupprober.py +++ b/libs/common/chardet/sbcsgroupprober.py @@ -27,47 +27,57 @@ ######################### END LICENSE BLOCK ######################### from .charsetgroupprober import CharSetGroupProber -from .sbcharsetprober import SingleByteCharSetProber -from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, - Latin5CyrillicModel, MacCyrillicModel, - Ibm866Model, Ibm855Model) -from .langgreekmodel import Latin7GreekModel, Win1253GreekModel -from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel -# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel -from .langthaimodel import TIS620ThaiModel -from .langhebrewmodel import Win1255HebrewModel from .hebrewprober import HebrewProber -from .langturkishmodel import Latin5TurkishModel +from .langbulgarianmodel import (ISO_8859_5_BULGARIAN_MODEL, + WINDOWS_1251_BULGARIAN_MODEL) +from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL +from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL +# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, +# WINDOWS_1250_HUNGARIAN_MODEL) +from .langrussianmodel import (IBM855_RUSSIAN_MODEL, IBM866_RUSSIAN_MODEL, + ISO_8859_5_RUSSIAN_MODEL, KOI8_R_RUSSIAN_MODEL, + MACCYRILLIC_RUSSIAN_MODEL, + WINDOWS_1251_RUSSIAN_MODEL) +from .langthaimodel import TIS_620_THAI_MODEL +from .langturkishmodel import ISO_8859_9_TURKISH_MODEL +from .sbcharsetprober import SingleByteCharSetProber class SBCSGroupProber(CharSetGroupProber): def __init__(self): super(SBCSGroupProber, self).__init__() + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, + False, hebrew_prober) + # TODO: See if using ISO-8859-8 Hebrew model works better here, since + # it's actually the visual one + visual_hebrew_prober = SingleByteCharSetProber(WINDOWS_1255_HEBREW_MODEL, + True, hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, + visual_hebrew_prober) + # TODO: ORDER MATTERS HERE. I changed the order vs what was in master + # and several tests failed that did not before. Some thought + # should be put into the ordering, and we should consider making + # order not matter here, because that is very counter-intuitive. self.probers = [ - SingleByteCharSetProber(Win1251CyrillicModel), - SingleByteCharSetProber(Koi8rModel), - SingleByteCharSetProber(Latin5CyrillicModel), - SingleByteCharSetProber(MacCyrillicModel), - SingleByteCharSetProber(Ibm866Model), - SingleByteCharSetProber(Ibm855Model), - SingleByteCharSetProber(Latin7GreekModel), - SingleByteCharSetProber(Win1253GreekModel), - SingleByteCharSetProber(Latin5BulgarianModel), - SingleByteCharSetProber(Win1251BulgarianModel), + SingleByteCharSetProber(WINDOWS_1251_RUSSIAN_MODEL), + SingleByteCharSetProber(KOI8_R_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_5_RUSSIAN_MODEL), + SingleByteCharSetProber(MACCYRILLIC_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM866_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM855_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_7_GREEK_MODEL), + SingleByteCharSetProber(WINDOWS_1253_GREEK_MODEL), + SingleByteCharSetProber(ISO_8859_5_BULGARIAN_MODEL), + SingleByteCharSetProber(WINDOWS_1251_BULGARIAN_MODEL), # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) # after we retrain model. - # SingleByteCharSetProber(Latin2HungarianModel), - # SingleByteCharSetProber(Win1250HungarianModel), - SingleByteCharSetProber(TIS620ThaiModel), - SingleByteCharSetProber(Latin5TurkishModel), + # SingleByteCharSetProber(ISO_8859_2_HUNGARIAN_MODEL), + # SingleByteCharSetProber(WINDOWS_1250_HUNGARIAN_MODEL), + SingleByteCharSetProber(TIS_620_THAI_MODEL), + SingleByteCharSetProber(ISO_8859_9_TURKISH_MODEL), + hebrew_prober, + logical_hebrew_prober, + visual_hebrew_prober, ] - hebrew_prober = HebrewProber() - logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, - False, hebrew_prober) - visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, - hebrew_prober) - hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) - self.probers.extend([hebrew_prober, logical_hebrew_prober, - visual_hebrew_prober]) - self.reset() diff --git a/libs/common/chardet/universaldetector.py b/libs/common/chardet/universaldetector.py index 7b4e92d6..055a8ac1 100644 --- a/libs/common/chardet/universaldetector.py +++ b/libs/common/chardet/universaldetector.py @@ -266,7 +266,7 @@ class UniversalDetector(object): 'language': max_prober.language} # Log all prober confidences if none met MINIMUM_THRESHOLD - if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.logger.getEffectiveLevel() <= logging.DEBUG: if self.result['encoding'] is None: self.logger.debug('no probers hit minimum threshold') for group_prober in self._charset_probers: @@ -280,7 +280,7 @@ class UniversalDetector(object): prober.get_confidence()) else: self.logger.debug('%s %s confidence = %s', - prober.charset_name, - prober.language, - prober.get_confidence()) + group_prober.charset_name, + group_prober.language, + group_prober.get_confidence()) return self.result diff --git a/libs/common/chardet/version.py b/libs/common/chardet/version.py index bb2a34a7..70369b9d 100644 --- a/libs/common/chardet/version.py +++ b/libs/common/chardet/version.py @@ -5,5 +5,5 @@ from within setup.py and from chardet subpackages. :author: Dan Blanchard (dan.blanchard@gmail.com) """ -__version__ = "3.0.4" +__version__ = "4.0.0" VERSION = __version__.split('.') diff --git a/libs/common/idna/core.py b/libs/common/idna/core.py index 104624ad..41ec5c71 100644 --- a/libs/common/idna/core.py +++ b/libs/common/idna/core.py @@ -9,7 +9,7 @@ _virama_combining_class = 9 _alabel_prefix = b'xn--' _unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') -if sys.version_info[0] == 3: +if sys.version_info[0] >= 3: unicode = str unichr = chr @@ -300,6 +300,10 @@ def ulabel(label): label = label.lower() if label.startswith(_alabel_prefix): label = label[len(_alabel_prefix):] + if not label: + raise IDNAError('Malformed A-label, no Punycode eligible content found') + if label.decode('ascii')[-1] == '-': + raise IDNAError('A-label must not end with a hyphen') else: check_label(label) return label.decode('ascii') diff --git a/libs/common/idna/idnadata.py b/libs/common/idna/idnadata.py index a80c959d..a284e4c8 100644 --- a/libs/common/idna/idnadata.py +++ b/libs/common/idna/idnadata.py @@ -1,6 +1,6 @@ # This file is automatically generated by tools/idna-data -__version__ = "11.0.0" +__version__ = "13.0.0" scripts = { 'Greek': ( 0x37000000374, @@ -48,16 +48,18 @@ scripts = { 0x300700003008, 0x30210000302a, 0x30380000303c, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xf9000000fa6e, 0xfa700000fada, - 0x200000002a6d7, + 0x16ff000016ff2, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, 0x2f8000002fa1e, + 0x300000003134b, ), 'Hebrew': ( 0x591000005c8, @@ -74,6 +76,7 @@ scripts = { 0x304100003097, 0x309d000030a0, 0x1b0010001b11f, + 0x1b1500001b153, 0x1f2000001f201, ), 'Katakana': ( @@ -85,6 +88,7 @@ scripts = { 0xff660000ff70, 0xff710000ff9e, 0x1b0000001b001, + 0x1b1640001b168, ), } joining_types = { @@ -387,9 +391,9 @@ joining_types = { 0x853: 68, 0x854: 82, 0x855: 68, - 0x856: 85, - 0x857: 85, - 0x858: 85, + 0x856: 82, + 0x857: 82, + 0x858: 82, 0x860: 68, 0x861: 85, 0x862: 68, @@ -430,6 +434,16 @@ joining_types = { 0x8bb: 68, 0x8bc: 68, 0x8bd: 68, + 0x8be: 68, + 0x8bf: 68, + 0x8c0: 68, + 0x8c1: 68, + 0x8c2: 68, + 0x8c3: 68, + 0x8c4: 68, + 0x8c5: 68, + 0x8c6: 68, + 0x8c7: 68, 0x8e2: 85, 0x1806: 85, 0x1807: 68, @@ -754,6 +768,34 @@ joining_types = { 0x10f52: 68, 0x10f53: 68, 0x10f54: 82, + 0x10fb0: 68, + 0x10fb1: 85, + 0x10fb2: 68, + 0x10fb3: 68, + 0x10fb4: 82, + 0x10fb5: 82, + 0x10fb6: 82, + 0x10fb7: 85, + 0x10fb8: 68, + 0x10fb9: 82, + 0x10fba: 82, + 0x10fbb: 68, + 0x10fbc: 68, + 0x10fbd: 82, + 0x10fbe: 68, + 0x10fbf: 68, + 0x10fc0: 85, + 0x10fc1: 68, + 0x10fc2: 82, + 0x10fc3: 82, + 0x10fc4: 68, + 0x10fc5: 85, + 0x10fc6: 85, + 0x10fc7: 85, + 0x10fc8: 85, + 0x10fc9: 82, + 0x10fca: 68, + 0x10fcb: 76, 0x110bd: 85, 0x110cd: 85, 0x1e900: 68, @@ -824,6 +866,7 @@ joining_types = { 0x1e941: 68, 0x1e942: 68, 0x1e943: 68, + 0x1e94b: 84, } codepoint_classes = { 'PVALID': ( @@ -1126,7 +1169,7 @@ codepoint_classes = { 0x8400000085c, 0x8600000086b, 0x8a0000008b5, - 0x8b6000008be, + 0x8b6000008c8, 0x8d3000008e2, 0x8e300000958, 0x96000000964, @@ -1185,7 +1228,7 @@ codepoint_classes = { 0xb3c00000b45, 0xb4700000b49, 0xb4b00000b4e, - 0xb5600000b58, + 0xb5500000b58, 0xb5f00000b64, 0xb6600000b70, 0xb7100000b72, @@ -1230,8 +1273,7 @@ codepoint_classes = { 0xce000000ce4, 0xce600000cf0, 0xcf100000cf3, - 0xd0000000d04, - 0xd0500000d0d, + 0xd0000000d0d, 0xd0e00000d11, 0xd1200000d45, 0xd4600000d49, @@ -1240,7 +1282,7 @@ codepoint_classes = { 0xd5f00000d64, 0xd6600000d70, 0xd7a00000d80, - 0xd8200000d84, + 0xd8100000d84, 0xd8500000d97, 0xd9a00000db2, 0xdb300000dbc, @@ -1258,18 +1300,11 @@ codepoint_classes = { 0xe5000000e5a, 0xe8100000e83, 0xe8400000e85, - 0xe8700000e89, - 0xe8a00000e8b, - 0xe8d00000e8e, - 0xe9400000e98, - 0xe9900000ea0, - 0xea100000ea4, + 0xe8600000e8b, + 0xe8c00000ea4, 0xea500000ea6, - 0xea700000ea8, - 0xeaa00000eac, - 0xead00000eb3, - 0xeb400000eba, - 0xebb00000ebe, + 0xea700000eb3, + 0xeb400000ebe, 0xec000000ec5, 0xec600000ec7, 0xec800000ece, @@ -1362,6 +1397,7 @@ codepoint_classes = { 0x1a9000001a9a, 0x1aa700001aa8, 0x1ab000001abe, + 0x1abf00001ac1, 0x1b0000001b4c, 0x1b5000001b5a, 0x1b6b00001b74, @@ -1370,7 +1406,7 @@ codepoint_classes = { 0x1c4000001c4a, 0x1c4d00001c7e, 0x1cd000001cd3, - 0x1cd400001cfa, + 0x1cd400001cfb, 0x1d0000001d2c, 0x1d2f00001d30, 0x1d3b00001d3c, @@ -1613,10 +1649,10 @@ codepoint_classes = { 0x30a1000030fb, 0x30fc000030ff, 0x310500003130, - 0x31a0000031bb, + 0x31a0000031c0, 0x31f000003200, - 0x340000004db6, - 0x4e0000009ff0, + 0x340000004dc0, + 0x4e0000009ffd, 0xa0000000a48d, 0xa4d00000a4fe, 0xa5000000a60d, @@ -1727,8 +1763,15 @@ codepoint_classes = { 0xa7b50000a7b6, 0xa7b70000a7b8, 0xa7b90000a7ba, - 0xa7f70000a7f8, + 0xa7bb0000a7bc, + 0xa7bd0000a7be, + 0xa7bf0000a7c0, + 0xa7c30000a7c4, + 0xa7c80000a7c9, + 0xa7ca0000a7cb, + 0xa7f60000a7f8, 0xa7fa0000a828, + 0xa82c0000a82d, 0xa8400000a874, 0xa8800000a8c6, 0xa8d00000a8da, @@ -1753,7 +1796,7 @@ codepoint_classes = { 0xab200000ab27, 0xab280000ab2f, 0xab300000ab5b, - 0xab600000ab66, + 0xab600000ab6a, 0xabc00000abeb, 0xabec0000abee, 0xabf00000abfa, @@ -1827,9 +1870,14 @@ codepoint_classes = { 0x10cc000010cf3, 0x10d0000010d28, 0x10d3000010d3a, + 0x10e8000010eaa, + 0x10eab00010ead, + 0x10eb000010eb2, 0x10f0000010f1d, 0x10f2700010f28, 0x10f3000010f51, + 0x10fb000010fc5, + 0x10fe000010ff7, 0x1100000011047, 0x1106600011070, 0x1107f000110bb, @@ -1837,12 +1885,12 @@ codepoint_classes = { 0x110f0000110fa, 0x1110000011135, 0x1113600011140, - 0x1114400011147, + 0x1114400011148, 0x1115000011174, 0x1117600011177, 0x11180000111c5, 0x111c9000111cd, - 0x111d0000111db, + 0x111ce000111db, 0x111dc000111dd, 0x1120000011212, 0x1121300011238, @@ -1871,7 +1919,7 @@ codepoint_classes = { 0x1137000011375, 0x114000001144b, 0x114500001145a, - 0x1145e0001145f, + 0x1145e00011462, 0x11480000114c6, 0x114c7000114c8, 0x114d0000114da, @@ -1881,18 +1929,28 @@ codepoint_classes = { 0x1160000011641, 0x1164400011645, 0x116500001165a, - 0x11680000116b8, + 0x11680000116b9, 0x116c0000116ca, 0x117000001171b, 0x1171d0001172c, 0x117300001173a, 0x118000001183b, 0x118c0000118ea, - 0x118ff00011900, + 0x118ff00011907, + 0x119090001190a, + 0x1190c00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193b00011944, + 0x119500001195a, + 0x119a0000119a8, + 0x119aa000119d8, + 0x119da000119e2, + 0x119e3000119e5, 0x11a0000011a3f, 0x11a4700011a48, - 0x11a5000011a84, - 0x11a8600011a9a, + 0x11a5000011a9a, 0x11a9d00011a9e, 0x11ac000011af9, 0x11c0000011c09, @@ -1916,6 +1974,7 @@ codepoint_classes = { 0x11d9300011d99, 0x11da000011daa, 0x11ee000011ef7, + 0x11fb000011fb1, 0x120000001239a, 0x1248000012544, 0x130000001342f, @@ -1931,13 +1990,18 @@ codepoint_classes = { 0x16b6300016b78, 0x16b7d00016b90, 0x16e6000016e80, - 0x16f0000016f45, - 0x16f5000016f7f, + 0x16f0000016f4b, + 0x16f4f00016f88, 0x16f8f00016fa0, 0x16fe000016fe2, - 0x17000000187f2, - 0x1880000018af3, + 0x16fe300016fe5, + 0x16ff000016ff2, + 0x17000000187f8, + 0x1880000018cd6, + 0x18d0000018d09, 0x1b0000001b11f, + 0x1b1500001b153, + 0x1b1640001b168, 0x1b1700001b2fc, 0x1bc000001bc6b, 0x1bc700001bc7d, @@ -1955,15 +2019,22 @@ codepoint_classes = { 0x1e01b0001e022, 0x1e0230001e025, 0x1e0260001e02b, + 0x1e1000001e12d, + 0x1e1300001e13e, + 0x1e1400001e14a, + 0x1e14e0001e14f, + 0x1e2c00001e2fa, 0x1e8000001e8c5, 0x1e8d00001e8d7, - 0x1e9220001e94b, + 0x1e9220001e94c, 0x1e9500001e95a, - 0x200000002a6d7, + 0x1fbf00001fbfa, + 0x200000002a6de, 0x2a7000002b735, 0x2b7400002b81e, 0x2b8200002cea2, 0x2ceb00002ebe1, + 0x300000003134b, ), 'CONTEXTJ': ( 0x200c0000200e, diff --git a/libs/common/idna/package_data.py b/libs/common/idna/package_data.py index 257e8989..ce1c521d 100644 --- a/libs/common/idna/package_data.py +++ b/libs/common/idna/package_data.py @@ -1,2 +1,2 @@ -__version__ = '2.8' +__version__ = '2.10' diff --git a/libs/common/idna/uts46data.py b/libs/common/idna/uts46data.py index a68ed4c0..3766dd49 100644 --- a/libs/common/idna/uts46data.py +++ b/libs/common/idna/uts46data.py @@ -4,7 +4,7 @@ """IDNA Mapping Table from UTS46.""" -__version__ = "11.0.0" +__version__ = "13.0.0" def _seg_0(): return [ (0x0, '3'), @@ -1074,7 +1074,7 @@ def _seg_10(): (0x8A0, 'V'), (0x8B5, 'X'), (0x8B6, 'V'), - (0x8BE, 'X'), + (0x8C8, 'X'), (0x8D3, 'V'), (0x8E2, 'X'), (0x8E3, 'V'), @@ -1205,7 +1205,7 @@ def _seg_11(): (0xB49, 'X'), (0xB4B, 'V'), (0xB4E, 'X'), - (0xB56, 'V'), + (0xB55, 'V'), (0xB58, 'X'), (0xB5C, 'M', u'ଡ଼'), (0xB5D, 'M', u'ଢ଼'), @@ -1272,7 +1272,7 @@ def _seg_12(): (0xC64, 'X'), (0xC66, 'V'), (0xC70, 'X'), - (0xC78, 'V'), + (0xC77, 'V'), (0xC8D, 'X'), (0xC8E, 'V'), (0xC91, 'X'), @@ -1299,8 +1299,6 @@ def _seg_12(): (0xCF1, 'V'), (0xCF3, 'X'), (0xD00, 'V'), - (0xD04, 'X'), - (0xD05, 'V'), (0xD0D, 'X'), (0xD0E, 'V'), (0xD11, 'X'), @@ -1314,7 +1312,7 @@ def _seg_12(): (0xD64, 'X'), (0xD66, 'V'), (0xD80, 'X'), - (0xD82, 'V'), + (0xD81, 'V'), (0xD84, 'X'), (0xD85, 'V'), (0xD97, 'X'), @@ -1348,33 +1346,19 @@ def _seg_12(): (0xE83, 'X'), (0xE84, 'V'), (0xE85, 'X'), - (0xE87, 'V'), - (0xE89, 'X'), - (0xE8A, 'V'), + (0xE86, 'V'), (0xE8B, 'X'), - (0xE8D, 'V'), - (0xE8E, 'X'), - (0xE94, 'V'), - ] - -def _seg_13(): - return [ - (0xE98, 'X'), - (0xE99, 'V'), - (0xEA0, 'X'), - (0xEA1, 'V'), + (0xE8C, 'V'), (0xEA4, 'X'), (0xEA5, 'V'), (0xEA6, 'X'), (0xEA7, 'V'), - (0xEA8, 'X'), - (0xEAA, 'V'), - (0xEAC, 'X'), - (0xEAD, 'V'), (0xEB3, 'M', u'ໍາ'), (0xEB4, 'V'), - (0xEBA, 'X'), - (0xEBB, 'V'), + ] + +def _seg_13(): + return [ (0xEBE, 'X'), (0xEC0, 'V'), (0xEC5, 'X'), @@ -1459,10 +1443,6 @@ def _seg_13(): (0x1260, 'V'), (0x1289, 'X'), (0x128A, 'V'), - ] - -def _seg_14(): - return [ (0x128E, 'X'), (0x1290, 'V'), (0x12B1, 'X'), @@ -1479,6 +1459,10 @@ def _seg_14(): (0x12D8, 'V'), (0x1311, 'X'), (0x1312, 'V'), + ] + +def _seg_14(): + return [ (0x1316, 'X'), (0x1318, 'V'), (0x135B, 'X'), @@ -1563,15 +1547,11 @@ def _seg_14(): (0x1A7F, 'V'), (0x1A8A, 'X'), (0x1A90, 'V'), - ] - -def _seg_15(): - return [ (0x1A9A, 'X'), (0x1AA0, 'V'), (0x1AAE, 'X'), (0x1AB0, 'V'), - (0x1ABF, 'X'), + (0x1AC1, 'X'), (0x1B00, 'V'), (0x1B4C, 'X'), (0x1B50, 'V'), @@ -1583,6 +1563,10 @@ def _seg_15(): (0x1C3B, 'V'), (0x1C4A, 'X'), (0x1C4D, 'V'), + ] + +def _seg_15(): + return [ (0x1C80, 'M', u'в'), (0x1C81, 'M', u'д'), (0x1C82, 'M', u'о'), @@ -1592,10 +1576,57 @@ def _seg_15(): (0x1C87, 'M', u'ѣ'), (0x1C88, 'M', u'ꙋ'), (0x1C89, 'X'), + (0x1C90, 'M', u'ა'), + (0x1C91, 'M', u'ბ'), + (0x1C92, 'M', u'გ'), + (0x1C93, 'M', u'დ'), + (0x1C94, 'M', u'ე'), + (0x1C95, 'M', u'ვ'), + (0x1C96, 'M', u'ზ'), + (0x1C97, 'M', u'თ'), + (0x1C98, 'M', u'ი'), + (0x1C99, 'M', u'კ'), + (0x1C9A, 'M', u'ლ'), + (0x1C9B, 'M', u'მ'), + (0x1C9C, 'M', u'ნ'), + (0x1C9D, 'M', u'ო'), + (0x1C9E, 'M', u'პ'), + (0x1C9F, 'M', u'ჟ'), + (0x1CA0, 'M', u'რ'), + (0x1CA1, 'M', u'ს'), + (0x1CA2, 'M', u'ტ'), + (0x1CA3, 'M', u'უ'), + (0x1CA4, 'M', u'ფ'), + (0x1CA5, 'M', u'ქ'), + (0x1CA6, 'M', u'ღ'), + (0x1CA7, 'M', u'ყ'), + (0x1CA8, 'M', u'შ'), + (0x1CA9, 'M', u'ჩ'), + (0x1CAA, 'M', u'ც'), + (0x1CAB, 'M', u'ძ'), + (0x1CAC, 'M', u'წ'), + (0x1CAD, 'M', u'ჭ'), + (0x1CAE, 'M', u'ხ'), + (0x1CAF, 'M', u'ჯ'), + (0x1CB0, 'M', u'ჰ'), + (0x1CB1, 'M', u'ჱ'), + (0x1CB2, 'M', u'ჲ'), + (0x1CB3, 'M', u'ჳ'), + (0x1CB4, 'M', u'ჴ'), + (0x1CB5, 'M', u'ჵ'), + (0x1CB6, 'M', u'ჶ'), + (0x1CB7, 'M', u'ჷ'), + (0x1CB8, 'M', u'ჸ'), + (0x1CB9, 'M', u'ჹ'), + (0x1CBA, 'M', u'ჺ'), + (0x1CBB, 'X'), + (0x1CBD, 'M', u'ჽ'), + (0x1CBE, 'M', u'ჾ'), + (0x1CBF, 'M', u'ჿ'), (0x1CC0, 'V'), (0x1CC8, 'X'), (0x1CD0, 'V'), - (0x1CFA, 'X'), + (0x1CFB, 'X'), (0x1D00, 'V'), (0x1D2C, 'M', u'a'), (0x1D2D, 'M', u'æ'), @@ -1636,6 +1667,10 @@ def _seg_15(): (0x1D50, 'M', u'm'), (0x1D51, 'M', u'ŋ'), (0x1D52, 'M', u'o'), + ] + +def _seg_16(): + return [ (0x1D53, 'M', u'ɔ'), (0x1D54, 'M', u'ᴖ'), (0x1D55, 'M', u'ᴗ'), @@ -1667,10 +1702,6 @@ def _seg_15(): (0x1D9C, 'M', u'c'), (0x1D9D, 'M', u'ɕ'), (0x1D9E, 'M', u'ð'), - ] - -def _seg_16(): - return [ (0x1D9F, 'M', u'ɜ'), (0x1DA0, 'M', u'f'), (0x1DA1, 'M', u'ɟ'), @@ -1740,6 +1771,10 @@ def _seg_16(): (0x1E1E, 'M', u'ḟ'), (0x1E1F, 'V'), (0x1E20, 'M', u'ḡ'), + ] + +def _seg_17(): + return [ (0x1E21, 'V'), (0x1E22, 'M', u'ḣ'), (0x1E23, 'V'), @@ -1771,10 +1806,6 @@ def _seg_16(): (0x1E3D, 'V'), (0x1E3E, 'M', u'ḿ'), (0x1E3F, 'V'), - ] - -def _seg_17(): - return [ (0x1E40, 'M', u'ṁ'), (0x1E41, 'V'), (0x1E42, 'M', u'ṃ'), @@ -1844,6 +1875,10 @@ def _seg_17(): (0x1E82, 'M', u'ẃ'), (0x1E83, 'V'), (0x1E84, 'M', u'ẅ'), + ] + +def _seg_18(): + return [ (0x1E85, 'V'), (0x1E86, 'M', u'ẇ'), (0x1E87, 'V'), @@ -1875,10 +1910,6 @@ def _seg_17(): (0x1EA6, 'M', u'ầ'), (0x1EA7, 'V'), (0x1EA8, 'M', u'ẩ'), - ] - -def _seg_18(): - return [ (0x1EA9, 'V'), (0x1EAA, 'M', u'ẫ'), (0x1EAB, 'V'), @@ -1948,6 +1979,10 @@ def _seg_18(): (0x1EEB, 'V'), (0x1EEC, 'M', u'ử'), (0x1EED, 'V'), + ] + +def _seg_19(): + return [ (0x1EEE, 'M', u'ữ'), (0x1EEF, 'V'), (0x1EF0, 'M', u'ự'), @@ -1979,10 +2014,6 @@ def _seg_18(): (0x1F18, 'M', u'ἐ'), (0x1F19, 'M', u'ἑ'), (0x1F1A, 'M', u'ἒ'), - ] - -def _seg_19(): - return [ (0x1F1B, 'M', u'ἓ'), (0x1F1C, 'M', u'ἔ'), (0x1F1D, 'M', u'ἕ'), @@ -2052,6 +2083,10 @@ def _seg_19(): (0x1F82, 'M', u'ἂι'), (0x1F83, 'M', u'ἃι'), (0x1F84, 'M', u'ἄι'), + ] + +def _seg_20(): + return [ (0x1F85, 'M', u'ἅι'), (0x1F86, 'M', u'ἆι'), (0x1F87, 'M', u'ἇι'), @@ -2083,10 +2118,6 @@ def _seg_19(): (0x1FA1, 'M', u'ὡι'), (0x1FA2, 'M', u'ὢι'), (0x1FA3, 'M', u'ὣι'), - ] - -def _seg_20(): - return [ (0x1FA4, 'M', u'ὤι'), (0x1FA5, 'M', u'ὥι'), (0x1FA6, 'M', u'ὦι'), @@ -2156,6 +2187,10 @@ def _seg_20(): (0x1FF0, 'X'), (0x1FF2, 'M', u'ὼι'), (0x1FF3, 'M', u'ωι'), + ] + +def _seg_21(): + return [ (0x1FF4, 'M', u'ώι'), (0x1FF5, 'X'), (0x1FF6, 'V'), @@ -2187,10 +2222,6 @@ def _seg_20(): (0x2035, 'V'), (0x2036, 'M', u'‵‵'), (0x2037, 'M', u'‵‵‵'), - ] - -def _seg_21(): - return [ (0x2038, 'V'), (0x203C, '3', u'!!'), (0x203D, 'V'), @@ -2260,6 +2291,10 @@ def _seg_21(): (0x20F1, 'X'), (0x2100, '3', u'a/c'), (0x2101, '3', u'a/s'), + ] + +def _seg_22(): + return [ (0x2102, 'M', u'c'), (0x2103, 'M', u'°c'), (0x2104, 'V'), @@ -2291,10 +2326,6 @@ def _seg_21(): (0x2127, 'V'), (0x2128, 'M', u'z'), (0x2129, 'V'), - ] - -def _seg_22(): - return [ (0x212A, 'M', u'k'), (0x212B, 'M', u'å'), (0x212C, 'M', u'b'), @@ -2364,6 +2395,10 @@ def _seg_22(): (0x2177, 'M', u'viii'), (0x2178, 'M', u'ix'), (0x2179, 'M', u'x'), + ] + +def _seg_23(): + return [ (0x217A, 'M', u'xi'), (0x217B, 'M', u'xii'), (0x217C, 'M', u'l'), @@ -2395,10 +2430,6 @@ def _seg_22(): (0x244B, 'X'), (0x2460, 'M', u'1'), (0x2461, 'M', u'2'), - ] - -def _seg_23(): - return [ (0x2462, 'M', u'3'), (0x2463, 'M', u'4'), (0x2464, 'M', u'5'), @@ -2468,6 +2499,10 @@ def _seg_23(): (0x24B7, 'M', u'b'), (0x24B8, 'M', u'c'), (0x24B9, 'M', u'd'), + ] + +def _seg_24(): + return [ (0x24BA, 'M', u'e'), (0x24BB, 'M', u'f'), (0x24BC, 'M', u'g'), @@ -2499,10 +2534,6 @@ def _seg_23(): (0x24D6, 'M', u'g'), (0x24D7, 'M', u'h'), (0x24D8, 'M', u'i'), - ] - -def _seg_24(): - return [ (0x24D9, 'M', u'j'), (0x24DA, 'M', u'k'), (0x24DB, 'M', u'l'), @@ -2533,10 +2564,7 @@ def _seg_24(): (0x2B74, 'X'), (0x2B76, 'V'), (0x2B96, 'X'), - (0x2B98, 'V'), - (0x2BC9, 'X'), - (0x2BCA, 'V'), - (0x2BFF, 'X'), + (0x2B97, 'V'), (0x2C00, 'M', u'ⰰ'), (0x2C01, 'M', u'ⰱ'), (0x2C02, 'M', u'ⰲ'), @@ -2575,6 +2603,10 @@ def _seg_24(): (0x2C23, 'M', u'ⱓ'), (0x2C24, 'M', u'ⱔ'), (0x2C25, 'M', u'ⱕ'), + ] + +def _seg_25(): + return [ (0x2C26, 'M', u'ⱖ'), (0x2C27, 'M', u'ⱗ'), (0x2C28, 'M', u'ⱘ'), @@ -2603,10 +2635,6 @@ def _seg_24(): (0x2C6E, 'M', u'ɱ'), (0x2C6F, 'M', u'ɐ'), (0x2C70, 'M', u'ɒ'), - ] - -def _seg_25(): - return [ (0x2C71, 'V'), (0x2C72, 'M', u'ⱳ'), (0x2C73, 'V'), @@ -2679,6 +2707,10 @@ def _seg_25(): (0x2CBC, 'M', u'ⲽ'), (0x2CBD, 'V'), (0x2CBE, 'M', u'ⲿ'), + ] + +def _seg_26(): + return [ (0x2CBF, 'V'), (0x2CC0, 'M', u'ⳁ'), (0x2CC1, 'V'), @@ -2707,10 +2739,6 @@ def _seg_25(): (0x2CD8, 'M', u'ⳙ'), (0x2CD9, 'V'), (0x2CDA, 'M', u'ⳛ'), - ] - -def _seg_26(): - return [ (0x2CDB, 'V'), (0x2CDC, 'M', u'ⳝ'), (0x2CDD, 'V'), @@ -2757,7 +2785,7 @@ def _seg_26(): (0x2DD8, 'V'), (0x2DDF, 'X'), (0x2DE0, 'V'), - (0x2E4F, 'X'), + (0x2E53, 'X'), (0x2E80, 'V'), (0x2E9A, 'X'), (0x2E9B, 'V'), @@ -2783,6 +2811,10 @@ def _seg_26(): (0x2F0F, 'M', u'几'), (0x2F10, 'M', u'凵'), (0x2F11, 'M', u'刀'), + ] + +def _seg_27(): + return [ (0x2F12, 'M', u'力'), (0x2F13, 'M', u'勹'), (0x2F14, 'M', u'匕'), @@ -2811,10 +2843,6 @@ def _seg_26(): (0x2F2B, 'M', u'尸'), (0x2F2C, 'M', u'屮'), (0x2F2D, 'M', u'山'), - ] - -def _seg_27(): - return [ (0x2F2E, 'M', u'巛'), (0x2F2F, 'M', u'工'), (0x2F30, 'M', u'己'), @@ -2887,6 +2915,10 @@ def _seg_27(): (0x2F73, 'M', u'穴'), (0x2F74, 'M', u'立'), (0x2F75, 'M', u'竹'), + ] + +def _seg_28(): + return [ (0x2F76, 'M', u'米'), (0x2F77, 'M', u'糸'), (0x2F78, 'M', u'缶'), @@ -2915,10 +2947,6 @@ def _seg_27(): (0x2F8F, 'M', u'行'), (0x2F90, 'M', u'衣'), (0x2F91, 'M', u'襾'), - ] - -def _seg_28(): - return [ (0x2F92, 'M', u'見'), (0x2F93, 'M', u'角'), (0x2F94, 'M', u'言'), @@ -2991,6 +3019,10 @@ def _seg_28(): (0x3000, '3', u' '), (0x3001, 'V'), (0x3002, 'M', u'.'), + ] + +def _seg_29(): + return [ (0x3003, 'V'), (0x3036, 'M', u'〒'), (0x3037, 'V'), @@ -3019,10 +3051,6 @@ def _seg_28(): (0x3136, 'M', u'ᆭ'), (0x3137, 'M', u'ᄃ'), (0x3138, 'M', u'ᄄ'), - ] - -def _seg_29(): - return [ (0x3139, 'M', u'ᄅ'), (0x313A, 'M', u'ᆰ'), (0x313B, 'M', u'ᆱ'), @@ -3095,6 +3123,10 @@ def _seg_29(): (0x317E, 'M', u'ᄶ'), (0x317F, 'M', u'ᅀ'), (0x3180, 'M', u'ᅇ'), + ] + +def _seg_30(): + return [ (0x3181, 'M', u'ᅌ'), (0x3182, 'M', u'ᇱ'), (0x3183, 'M', u'ᇲ'), @@ -3123,15 +3155,9 @@ def _seg_29(): (0x319B, 'M', u'丙'), (0x319C, 'M', u'丁'), (0x319D, 'M', u'天'), - ] - -def _seg_30(): - return [ (0x319E, 'M', u'地'), (0x319F, 'M', u'人'), (0x31A0, 'V'), - (0x31BB, 'X'), - (0x31C0, 'V'), (0x31E4, 'X'), (0x31F0, 'V'), (0x3200, '3', u'(ᄀ)'), @@ -3201,6 +3227,10 @@ def _seg_30(): (0x3240, '3', u'(祭)'), (0x3241, '3', u'(休)'), (0x3242, '3', u'(自)'), + ] + +def _seg_31(): + return [ (0x3243, '3', u'(至)'), (0x3244, 'M', u'問'), (0x3245, 'M', u'幼'), @@ -3227,10 +3257,6 @@ def _seg_30(): (0x3261, 'M', u'ᄂ'), (0x3262, 'M', u'ᄃ'), (0x3263, 'M', u'ᄅ'), - ] - -def _seg_31(): - return [ (0x3264, 'M', u'ᄆ'), (0x3265, 'M', u'ᄇ'), (0x3266, 'M', u'ᄉ'), @@ -3305,6 +3331,10 @@ def _seg_31(): (0x32AB, 'M', u'学'), (0x32AC, 'M', u'監'), (0x32AD, 'M', u'企'), + ] + +def _seg_32(): + return [ (0x32AE, 'M', u'資'), (0x32AF, 'M', u'協'), (0x32B0, 'M', u'夜'), @@ -3331,10 +3361,6 @@ def _seg_31(): (0x32C5, 'M', u'6月'), (0x32C6, 'M', u'7月'), (0x32C7, 'M', u'8月'), - ] - -def _seg_32(): - return [ (0x32C8, 'M', u'9月'), (0x32C9, 'M', u'10月'), (0x32CA, 'M', u'11月'), @@ -3390,7 +3416,7 @@ def _seg_32(): (0x32FC, 'M', u'ヰ'), (0x32FD, 'M', u'ヱ'), (0x32FE, 'M', u'ヲ'), - (0x32FF, 'X'), + (0x32FF, 'M', u'令和'), (0x3300, 'M', u'アパート'), (0x3301, 'M', u'アルファ'), (0x3302, 'M', u'アンペア'), @@ -3409,6 +3435,10 @@ def _seg_32(): (0x330F, 'M', u'ガンマ'), (0x3310, 'M', u'ギガ'), (0x3311, 'M', u'ギニー'), + ] + +def _seg_33(): + return [ (0x3312, 'M', u'キュリー'), (0x3313, 'M', u'ギルダー'), (0x3314, 'M', u'キロ'), @@ -3435,10 +3465,6 @@ def _seg_32(): (0x3329, 'M', u'ノット'), (0x332A, 'M', u'ハイツ'), (0x332B, 'M', u'パーセント'), - ] - -def _seg_33(): - return [ (0x332C, 'M', u'パーツ'), (0x332D, 'M', u'バーレル'), (0x332E, 'M', u'ピアストル'), @@ -3513,6 +3539,10 @@ def _seg_33(): (0x3373, 'M', u'au'), (0x3374, 'M', u'bar'), (0x3375, 'M', u'ov'), + ] + +def _seg_34(): + return [ (0x3376, 'M', u'pc'), (0x3377, 'M', u'dm'), (0x3378, 'M', u'dm2'), @@ -3539,10 +3569,6 @@ def _seg_33(): (0x338D, 'M', u'μg'), (0x338E, 'M', u'mg'), (0x338F, 'M', u'kg'), - ] - -def _seg_34(): - return [ (0x3390, 'M', u'hz'), (0x3391, 'M', u'khz'), (0x3392, 'M', u'mhz'), @@ -3617,6 +3643,10 @@ def _seg_34(): (0x33D7, 'M', u'ph'), (0x33D8, 'X'), (0x33D9, 'M', u'ppm'), + ] + +def _seg_35(): + return [ (0x33DA, 'M', u'pr'), (0x33DB, 'M', u'sr'), (0x33DC, 'M', u'sv'), @@ -3643,10 +3673,6 @@ def _seg_34(): (0x33F1, 'M', u'18日'), (0x33F2, 'M', u'19日'), (0x33F3, 'M', u'20日'), - ] - -def _seg_35(): - return [ (0x33F4, 'M', u'21日'), (0x33F5, 'M', u'22日'), (0x33F6, 'M', u'23日'), @@ -3660,9 +3686,7 @@ def _seg_35(): (0x33FE, 'M', u'31日'), (0x33FF, 'M', u'gal'), (0x3400, 'V'), - (0x4DB6, 'X'), - (0x4DC0, 'V'), - (0x9FF0, 'X'), + (0x9FFD, 'X'), (0xA000, 'V'), (0xA48D, 'X'), (0xA490, 'V'), @@ -3723,6 +3747,10 @@ def _seg_35(): (0xA685, 'V'), (0xA686, 'M', u'ꚇ'), (0xA687, 'V'), + ] + +def _seg_36(): + return [ (0xA688, 'M', u'ꚉ'), (0xA689, 'V'), (0xA68A, 'M', u'ꚋ'), @@ -3747,10 +3775,6 @@ def _seg_35(): (0xA69D, 'M', u'ь'), (0xA69E, 'V'), (0xA6F8, 'X'), - ] - -def _seg_36(): - return [ (0xA700, 'V'), (0xA722, 'M', u'ꜣ'), (0xA723, 'V'), @@ -3827,6 +3851,10 @@ def _seg_36(): (0xA76C, 'M', u'ꝭ'), (0xA76D, 'V'), (0xA76E, 'M', u'ꝯ'), + ] + +def _seg_37(): + return [ (0xA76F, 'V'), (0xA770, 'M', u'ꝯ'), (0xA771, 'V'), @@ -3851,10 +3879,6 @@ def _seg_36(): (0xA78E, 'V'), (0xA790, 'M', u'ꞑ'), (0xA791, 'V'), - ] - -def _seg_37(): - return [ (0xA792, 'M', u'ꞓ'), (0xA793, 'V'), (0xA796, 'M', u'ꞗ'), @@ -3891,14 +3915,31 @@ def _seg_37(): (0xA7B5, 'V'), (0xA7B6, 'M', u'ꞷ'), (0xA7B7, 'V'), - (0xA7B8, 'X'), + (0xA7B8, 'M', u'ꞹ'), (0xA7B9, 'V'), - (0xA7BA, 'X'), - (0xA7F7, 'V'), + (0xA7BA, 'M', u'ꞻ'), + (0xA7BB, 'V'), + (0xA7BC, 'M', u'ꞽ'), + (0xA7BD, 'V'), + (0xA7BE, 'M', u'ꞿ'), + (0xA7BF, 'V'), + (0xA7C0, 'X'), + (0xA7C2, 'M', u'ꟃ'), + (0xA7C3, 'V'), + (0xA7C4, 'M', u'ꞔ'), + (0xA7C5, 'M', u'ʂ'), + (0xA7C6, 'M', u'ᶎ'), + (0xA7C7, 'M', u'ꟈ'), + (0xA7C8, 'V'), + (0xA7C9, 'M', u'ꟊ'), + (0xA7CA, 'V'), + (0xA7CB, 'X'), + (0xA7F5, 'M', u'ꟶ'), + (0xA7F6, 'V'), (0xA7F8, 'M', u'ħ'), (0xA7F9, 'M', u'œ'), (0xA7FA, 'V'), - (0xA82C, 'X'), + (0xA82D, 'X'), (0xA830, 'V'), (0xA83A, 'X'), (0xA840, 'V'), @@ -3914,6 +3955,10 @@ def _seg_37(): (0xA980, 'V'), (0xA9CE, 'X'), (0xA9CF, 'V'), + ] + +def _seg_38(): + return [ (0xA9DA, 'X'), (0xA9DE, 'V'), (0xA9FF, 'X'), @@ -3943,7 +3988,9 @@ def _seg_37(): (0xAB5E, 'M', u'ɫ'), (0xAB5F, 'M', u'ꭒ'), (0xAB60, 'V'), - (0xAB66, 'X'), + (0xAB69, 'M', u'ʍ'), + (0xAB6A, 'V'), + (0xAB6C, 'X'), (0xAB70, 'M', u'Ꭰ'), (0xAB71, 'M', u'Ꭱ'), (0xAB72, 'M', u'Ꭲ'), @@ -3955,10 +4002,6 @@ def _seg_37(): (0xAB78, 'M', u'Ꭸ'), (0xAB79, 'M', u'Ꭹ'), (0xAB7A, 'M', u'Ꭺ'), - ] - -def _seg_38(): - return [ (0xAB7B, 'M', u'Ꭻ'), (0xAB7C, 'M', u'Ꭼ'), (0xAB7D, 'M', u'Ꭽ'), @@ -4016,6 +4059,10 @@ def _seg_38(): (0xABB1, 'M', u'Ꮱ'), (0xABB2, 'M', u'Ꮲ'), (0xABB3, 'M', u'Ꮳ'), + ] + +def _seg_39(): + return [ (0xABB4, 'M', u'Ꮴ'), (0xABB5, 'M', u'Ꮵ'), (0xABB6, 'M', u'Ꮶ'), @@ -4059,10 +4106,6 @@ def _seg_38(): (0xF913, 'M', u'邏'), (0xF914, 'M', u'樂'), (0xF915, 'M', u'洛'), - ] - -def _seg_39(): - return [ (0xF916, 'M', u'烙'), (0xF917, 'M', u'珞'), (0xF918, 'M', u'落'), @@ -4120,6 +4163,10 @@ def _seg_39(): (0xF94C, 'M', u'樓'), (0xF94D, 'M', u'淚'), (0xF94E, 'M', u'漏'), + ] + +def _seg_40(): + return [ (0xF94F, 'M', u'累'), (0xF950, 'M', u'縷'), (0xF951, 'M', u'陋'), @@ -4163,10 +4210,6 @@ def _seg_39(): (0xF977, 'M', u'亮'), (0xF978, 'M', u'兩'), (0xF979, 'M', u'凉'), - ] - -def _seg_40(): - return [ (0xF97A, 'M', u'梁'), (0xF97B, 'M', u'糧'), (0xF97C, 'M', u'良'), @@ -4224,6 +4267,10 @@ def _seg_40(): (0xF9B0, 'M', u'聆'), (0xF9B1, 'M', u'鈴'), (0xF9B2, 'M', u'零'), + ] + +def _seg_41(): + return [ (0xF9B3, 'M', u'靈'), (0xF9B4, 'M', u'領'), (0xF9B5, 'M', u'例'), @@ -4267,10 +4314,6 @@ def _seg_40(): (0xF9DB, 'M', u'率'), (0xF9DC, 'M', u'隆'), (0xF9DD, 'M', u'利'), - ] - -def _seg_41(): - return [ (0xF9DE, 'M', u'吏'), (0xF9DF, 'M', u'履'), (0xF9E0, 'M', u'易'), @@ -4328,6 +4371,10 @@ def _seg_41(): (0xFA16, 'M', u'猪'), (0xFA17, 'M', u'益'), (0xFA18, 'M', u'礼'), + ] + +def _seg_42(): + return [ (0xFA19, 'M', u'神'), (0xFA1A, 'M', u'祥'), (0xFA1B, 'M', u'福'), @@ -4371,10 +4418,6 @@ def _seg_41(): (0xFA44, 'M', u'梅'), (0xFA45, 'M', u'海'), (0xFA46, 'M', u'渚'), - ] - -def _seg_42(): - return [ (0xFA47, 'M', u'漢'), (0xFA48, 'M', u'煮'), (0xFA49, 'M', u'爫'), @@ -4432,6 +4475,10 @@ def _seg_42(): (0xFA7F, 'M', u'奔'), (0xFA80, 'M', u'婢'), (0xFA81, 'M', u'嬨'), + ] + +def _seg_43(): + return [ (0xFA82, 'M', u'廒'), (0xFA83, 'M', u'廙'), (0xFA84, 'M', u'彩'), @@ -4475,10 +4522,6 @@ def _seg_42(): (0xFAAA, 'M', u'着'), (0xFAAB, 'M', u'磌'), (0xFAAC, 'M', u'窱'), - ] - -def _seg_43(): - return [ (0xFAAD, 'M', u'節'), (0xFAAE, 'M', u'类'), (0xFAAF, 'M', u'絛'), @@ -4536,6 +4579,10 @@ def _seg_43(): (0xFB14, 'M', u'մե'), (0xFB15, 'M', u'մի'), (0xFB16, 'M', u'վն'), + ] + +def _seg_44(): + return [ (0xFB17, 'M', u'մխ'), (0xFB18, 'X'), (0xFB1D, 'M', u'יִ'), @@ -4579,10 +4626,6 @@ def _seg_43(): (0xFB43, 'M', u'ףּ'), (0xFB44, 'M', u'פּ'), (0xFB45, 'X'), - ] - -def _seg_44(): - return [ (0xFB46, 'M', u'צּ'), (0xFB47, 'M', u'קּ'), (0xFB48, 'M', u'רּ'), @@ -4640,6 +4683,10 @@ def _seg_44(): (0xFBEE, 'M', u'ئو'), (0xFBF0, 'M', u'ئۇ'), (0xFBF2, 'M', u'ئۆ'), + ] + +def _seg_45(): + return [ (0xFBF4, 'M', u'ئۈ'), (0xFBF6, 'M', u'ئې'), (0xFBF9, 'M', u'ئى'), @@ -4683,10 +4730,6 @@ def _seg_44(): (0xFC24, 'M', u'ضخ'), (0xFC25, 'M', u'ضم'), (0xFC26, 'M', u'طح'), - ] - -def _seg_45(): - return [ (0xFC27, 'M', u'طم'), (0xFC28, 'M', u'ظم'), (0xFC29, 'M', u'عج'), @@ -4744,6 +4787,10 @@ def _seg_45(): (0xFC5D, 'M', u'ىٰ'), (0xFC5E, '3', u' ٌّ'), (0xFC5F, '3', u' ٍّ'), + ] + +def _seg_46(): + return [ (0xFC60, '3', u' َّ'), (0xFC61, '3', u' ُّ'), (0xFC62, '3', u' ِّ'), @@ -4787,10 +4834,6 @@ def _seg_45(): (0xFC88, 'M', u'ما'), (0xFC89, 'M', u'مم'), (0xFC8A, 'M', u'نر'), - ] - -def _seg_46(): - return [ (0xFC8B, 'M', u'نز'), (0xFC8C, 'M', u'نم'), (0xFC8D, 'M', u'نن'), @@ -4848,6 +4891,10 @@ def _seg_46(): (0xFCC1, 'M', u'فم'), (0xFCC2, 'M', u'قح'), (0xFCC3, 'M', u'قم'), + ] + +def _seg_47(): + return [ (0xFCC4, 'M', u'كج'), (0xFCC5, 'M', u'كح'), (0xFCC6, 'M', u'كخ'), @@ -4891,10 +4938,6 @@ def _seg_46(): (0xFCEC, 'M', u'كم'), (0xFCED, 'M', u'لم'), (0xFCEE, 'M', u'نم'), - ] - -def _seg_47(): - return [ (0xFCEF, 'M', u'نه'), (0xFCF0, 'M', u'يم'), (0xFCF1, 'M', u'يه'), @@ -4952,6 +4995,10 @@ def _seg_47(): (0xFD25, 'M', u'شج'), (0xFD26, 'M', u'شح'), (0xFD27, 'M', u'شخ'), + ] + +def _seg_48(): + return [ (0xFD28, 'M', u'شم'), (0xFD29, 'M', u'شر'), (0xFD2A, 'M', u'سر'), @@ -4995,10 +5042,6 @@ def _seg_47(): (0xFD66, 'M', u'صمم'), (0xFD67, 'M', u'شحم'), (0xFD69, 'M', u'شجي'), - ] - -def _seg_48(): - return [ (0xFD6A, 'M', u'شمخ'), (0xFD6C, 'M', u'شمم'), (0xFD6E, 'M', u'ضحى'), @@ -5056,6 +5099,10 @@ def _seg_48(): (0xFDAC, 'M', u'لجي'), (0xFDAD, 'M', u'لمي'), (0xFDAE, 'M', u'يحي'), + ] + +def _seg_49(): + return [ (0xFDAF, 'M', u'يجي'), (0xFDB0, 'M', u'يمي'), (0xFDB1, 'M', u'ممي'), @@ -5099,10 +5146,6 @@ def _seg_48(): (0xFDFE, 'X'), (0xFE00, 'I'), (0xFE10, '3', u','), - ] - -def _seg_49(): - return [ (0xFE11, 'M', u'、'), (0xFE12, 'X'), (0xFE13, '3', u':'), @@ -5160,6 +5203,10 @@ def _seg_49(): (0xFE64, '3', u'<'), (0xFE65, '3', u'>'), (0xFE66, '3', u'='), + ] + +def _seg_50(): + return [ (0xFE67, 'X'), (0xFE68, '3', u'\\'), (0xFE69, '3', u'$'), @@ -5203,10 +5250,6 @@ def _seg_49(): (0xFEB1, 'M', u'س'), (0xFEB5, 'M', u'ش'), (0xFEB9, 'M', u'ص'), - ] - -def _seg_50(): - return [ (0xFEBD, 'M', u'ض'), (0xFEC1, 'M', u'ط'), (0xFEC5, 'M', u'ظ'), @@ -5264,6 +5307,10 @@ def _seg_50(): (0xFF21, 'M', u'a'), (0xFF22, 'M', u'b'), (0xFF23, 'M', u'c'), + ] + +def _seg_51(): + return [ (0xFF24, 'M', u'd'), (0xFF25, 'M', u'e'), (0xFF26, 'M', u'f'), @@ -5307,10 +5354,6 @@ def _seg_50(): (0xFF4C, 'M', u'l'), (0xFF4D, 'M', u'm'), (0xFF4E, 'M', u'n'), - ] - -def _seg_51(): - return [ (0xFF4F, 'M', u'o'), (0xFF50, 'M', u'p'), (0xFF51, 'M', u'q'), @@ -5368,6 +5411,10 @@ def _seg_51(): (0xFF85, 'M', u'ナ'), (0xFF86, 'M', u'ニ'), (0xFF87, 'M', u'ヌ'), + ] + +def _seg_52(): + return [ (0xFF88, 'M', u'ネ'), (0xFF89, 'M', u'ノ'), (0xFF8A, 'M', u'ハ'), @@ -5411,10 +5458,6 @@ def _seg_51(): (0xFFB0, 'M', u'ᄚ'), (0xFFB1, 'M', u'ᄆ'), (0xFFB2, 'M', u'ᄇ'), - ] - -def _seg_52(): - return [ (0xFFB3, 'M', u'ᄈ'), (0xFFB4, 'M', u'ᄡ'), (0xFFB5, 'M', u'ᄉ'), @@ -5472,6 +5515,10 @@ def _seg_52(): (0x10000, 'V'), (0x1000C, 'X'), (0x1000D, 'V'), + ] + +def _seg_53(): + return [ (0x10027, 'X'), (0x10028, 'V'), (0x1003B, 'X'), @@ -5490,7 +5537,7 @@ def _seg_52(): (0x10137, 'V'), (0x1018F, 'X'), (0x10190, 'V'), - (0x1019C, 'X'), + (0x1019D, 'X'), (0x101A0, 'V'), (0x101A1, 'X'), (0x101D0, 'V'), @@ -5515,10 +5562,6 @@ def _seg_52(): (0x103D6, 'X'), (0x10400, 'M', u'𐐨'), (0x10401, 'M', u'𐐩'), - ] - -def _seg_53(): - return [ (0x10402, 'M', u'𐐪'), (0x10403, 'M', u'𐐫'), (0x10404, 'M', u'𐐬'), @@ -5576,6 +5619,10 @@ def _seg_53(): (0x104BC, 'M', u'𐓤'), (0x104BD, 'M', u'𐓥'), (0x104BE, 'M', u'𐓦'), + ] + +def _seg_54(): + return [ (0x104BF, 'M', u'𐓧'), (0x104C0, 'M', u'𐓨'), (0x104C1, 'M', u'𐓩'), @@ -5619,10 +5666,6 @@ def _seg_53(): (0x1080A, 'V'), (0x10836, 'X'), (0x10837, 'V'), - ] - -def _seg_54(): - return [ (0x10839, 'X'), (0x1083C, 'V'), (0x1083D, 'X'), @@ -5680,6 +5723,10 @@ def _seg_54(): (0x10B9D, 'X'), (0x10BA9, 'V'), (0x10BB0, 'X'), + ] + +def _seg_55(): + return [ (0x10C00, 'V'), (0x10C49, 'X'), (0x10C80, 'M', u'𐳀'), @@ -5723,10 +5770,6 @@ def _seg_54(): (0x10CA6, 'M', u'𐳦'), (0x10CA7, 'M', u'𐳧'), (0x10CA8, 'M', u'𐳨'), - ] - -def _seg_55(): - return [ (0x10CA9, 'M', u'𐳩'), (0x10CAA, 'M', u'𐳪'), (0x10CAB, 'M', u'𐳫'), @@ -5746,10 +5789,20 @@ def _seg_55(): (0x10D3A, 'X'), (0x10E60, 'V'), (0x10E7F, 'X'), + (0x10E80, 'V'), + (0x10EAA, 'X'), + (0x10EAB, 'V'), + (0x10EAE, 'X'), + (0x10EB0, 'V'), + (0x10EB2, 'X'), (0x10F00, 'V'), (0x10F28, 'X'), (0x10F30, 'V'), (0x10F5A, 'X'), + (0x10FB0, 'V'), + (0x10FCC, 'X'), + (0x10FE0, 'V'), + (0x10FF7, 'X'), (0x11000, 'V'), (0x1104E, 'X'), (0x11052, 'V'), @@ -5765,17 +5818,19 @@ def _seg_55(): (0x11100, 'V'), (0x11135, 'X'), (0x11136, 'V'), - (0x11147, 'X'), + (0x11148, 'X'), (0x11150, 'V'), (0x11177, 'X'), (0x11180, 'V'), - (0x111CE, 'X'), - (0x111D0, 'V'), (0x111E0, 'X'), (0x111E1, 'V'), (0x111F5, 'X'), (0x11200, 'V'), (0x11212, 'X'), + ] + +def _seg_56(): + return [ (0x11213, 'V'), (0x1123F, 'X'), (0x11280, 'V'), @@ -5823,15 +5878,9 @@ def _seg_55(): (0x11370, 'V'), (0x11375, 'X'), (0x11400, 'V'), - (0x1145A, 'X'), - (0x1145B, 'V'), (0x1145C, 'X'), (0x1145D, 'V'), - ] - -def _seg_56(): - return [ - (0x1145F, 'X'), + (0x11462, 'X'), (0x11480, 'V'), (0x114C8, 'X'), (0x114D0, 'V'), @@ -5847,7 +5896,7 @@ def _seg_56(): (0x11660, 'V'), (0x1166D, 'X'), (0x11680, 'V'), - (0x116B8, 'X'), + (0x116B9, 'X'), (0x116C0, 'V'), (0x116CA, 'X'), (0x11700, 'V'), @@ -5882,6 +5931,10 @@ def _seg_56(): (0x118B5, 'M', u'𑣕'), (0x118B6, 'M', u'𑣖'), (0x118B7, 'M', u'𑣗'), + ] + +def _seg_57(): + return [ (0x118B8, 'M', u'𑣘'), (0x118B9, 'M', u'𑣙'), (0x118BA, 'M', u'𑣚'), @@ -5893,12 +5946,30 @@ def _seg_56(): (0x118C0, 'V'), (0x118F3, 'X'), (0x118FF, 'V'), - (0x11900, 'X'), + (0x11907, 'X'), + (0x11909, 'V'), + (0x1190A, 'X'), + (0x1190C, 'V'), + (0x11914, 'X'), + (0x11915, 'V'), + (0x11917, 'X'), + (0x11918, 'V'), + (0x11936, 'X'), + (0x11937, 'V'), + (0x11939, 'X'), + (0x1193B, 'V'), + (0x11947, 'X'), + (0x11950, 'V'), + (0x1195A, 'X'), + (0x119A0, 'V'), + (0x119A8, 'X'), + (0x119AA, 'V'), + (0x119D8, 'X'), + (0x119DA, 'V'), + (0x119E5, 'X'), (0x11A00, 'V'), (0x11A48, 'X'), (0x11A50, 'V'), - (0x11A84, 'X'), - (0x11A86, 'V'), (0x11AA3, 'X'), (0x11AC0, 'V'), (0x11AF9, 'X'), @@ -5931,10 +6002,6 @@ def _seg_56(): (0x11D50, 'V'), (0x11D5A, 'X'), (0x11D60, 'V'), - ] - -def _seg_57(): - return [ (0x11D66, 'X'), (0x11D67, 'V'), (0x11D69, 'X'), @@ -5948,7 +6015,11 @@ def _seg_57(): (0x11DAA, 'X'), (0x11EE0, 'V'), (0x11EF9, 'X'), - (0x12000, 'V'), + (0x11FB0, 'V'), + (0x11FB1, 'X'), + (0x11FC0, 'V'), + (0x11FF2, 'X'), + (0x11FFF, 'V'), (0x1239A, 'X'), (0x12400, 'V'), (0x1246F, 'X'), @@ -5964,6 +6035,10 @@ def _seg_57(): (0x16A39, 'X'), (0x16A40, 'V'), (0x16A5F, 'X'), + ] + +def _seg_58(): + return [ (0x16A60, 'V'), (0x16A6A, 'X'), (0x16A6E, 'V'), @@ -5982,22 +6057,62 @@ def _seg_57(): (0x16B78, 'X'), (0x16B7D, 'V'), (0x16B90, 'X'), + (0x16E40, 'M', u'𖹠'), + (0x16E41, 'M', u'𖹡'), + (0x16E42, 'M', u'𖹢'), + (0x16E43, 'M', u'𖹣'), + (0x16E44, 'M', u'𖹤'), + (0x16E45, 'M', u'𖹥'), + (0x16E46, 'M', u'𖹦'), + (0x16E47, 'M', u'𖹧'), + (0x16E48, 'M', u'𖹨'), + (0x16E49, 'M', u'𖹩'), + (0x16E4A, 'M', u'𖹪'), + (0x16E4B, 'M', u'𖹫'), + (0x16E4C, 'M', u'𖹬'), + (0x16E4D, 'M', u'𖹭'), + (0x16E4E, 'M', u'𖹮'), + (0x16E4F, 'M', u'𖹯'), + (0x16E50, 'M', u'𖹰'), + (0x16E51, 'M', u'𖹱'), + (0x16E52, 'M', u'𖹲'), + (0x16E53, 'M', u'𖹳'), + (0x16E54, 'M', u'𖹴'), + (0x16E55, 'M', u'𖹵'), + (0x16E56, 'M', u'𖹶'), + (0x16E57, 'M', u'𖹷'), + (0x16E58, 'M', u'𖹸'), + (0x16E59, 'M', u'𖹹'), + (0x16E5A, 'M', u'𖹺'), + (0x16E5B, 'M', u'𖹻'), + (0x16E5C, 'M', u'𖹼'), + (0x16E5D, 'M', u'𖹽'), + (0x16E5E, 'M', u'𖹾'), + (0x16E5F, 'M', u'𖹿'), (0x16E60, 'V'), (0x16E9B, 'X'), (0x16F00, 'V'), - (0x16F45, 'X'), - (0x16F50, 'V'), - (0x16F7F, 'X'), + (0x16F4B, 'X'), + (0x16F4F, 'V'), + (0x16F88, 'X'), (0x16F8F, 'V'), (0x16FA0, 'X'), (0x16FE0, 'V'), - (0x16FE2, 'X'), + (0x16FE5, 'X'), + (0x16FF0, 'V'), + (0x16FF2, 'X'), (0x17000, 'V'), - (0x187F2, 'X'), + (0x187F8, 'X'), (0x18800, 'V'), - (0x18AF3, 'X'), + (0x18CD6, 'X'), + (0x18D00, 'V'), + (0x18D09, 'X'), (0x1B000, 'V'), (0x1B11F, 'X'), + (0x1B150, 'V'), + (0x1B153, 'X'), + (0x1B164, 'V'), + (0x1B168, 'X'), (0x1B170, 'V'), (0x1B2FC, 'X'), (0x1BC00, 'V'), @@ -6024,6 +6139,10 @@ def _seg_57(): (0x1D163, 'M', u'𝅘𝅥𝅱'), (0x1D164, 'M', u'𝅘𝅥𝅲'), (0x1D165, 'V'), + ] + +def _seg_59(): + return [ (0x1D173, 'X'), (0x1D17B, 'V'), (0x1D1BB, 'M', u'𝆹𝅥'), @@ -6035,10 +6154,6 @@ def _seg_57(): (0x1D1C1, 'V'), (0x1D1E9, 'X'), (0x1D200, 'V'), - ] - -def _seg_58(): - return [ (0x1D246, 'X'), (0x1D2E0, 'V'), (0x1D2F4, 'X'), @@ -6128,6 +6243,10 @@ def _seg_58(): (0x1D44F, 'M', u'b'), (0x1D450, 'M', u'c'), (0x1D451, 'M', u'd'), + ] + +def _seg_60(): + return [ (0x1D452, 'M', u'e'), (0x1D453, 'M', u'f'), (0x1D454, 'M', u'g'), @@ -6139,10 +6258,6 @@ def _seg_58(): (0x1D45A, 'M', u'm'), (0x1D45B, 'M', u'n'), (0x1D45C, 'M', u'o'), - ] - -def _seg_59(): - return [ (0x1D45D, 'M', u'p'), (0x1D45E, 'M', u'q'), (0x1D45F, 'M', u'r'), @@ -6232,6 +6347,10 @@ def _seg_59(): (0x1D4B6, 'M', u'a'), (0x1D4B7, 'M', u'b'), (0x1D4B8, 'M', u'c'), + ] + +def _seg_61(): + return [ (0x1D4B9, 'M', u'd'), (0x1D4BA, 'X'), (0x1D4BB, 'M', u'f'), @@ -6243,10 +6362,6 @@ def _seg_59(): (0x1D4C1, 'M', u'l'), (0x1D4C2, 'M', u'm'), (0x1D4C3, 'M', u'n'), - ] - -def _seg_60(): - return [ (0x1D4C4, 'X'), (0x1D4C5, 'M', u'p'), (0x1D4C6, 'M', u'q'), @@ -6336,6 +6451,10 @@ def _seg_60(): (0x1D51B, 'M', u'x'), (0x1D51C, 'M', u'y'), (0x1D51D, 'X'), + ] + +def _seg_62(): + return [ (0x1D51E, 'M', u'a'), (0x1D51F, 'M', u'b'), (0x1D520, 'M', u'c'), @@ -6347,10 +6466,6 @@ def _seg_60(): (0x1D526, 'M', u'i'), (0x1D527, 'M', u'j'), (0x1D528, 'M', u'k'), - ] - -def _seg_61(): - return [ (0x1D529, 'M', u'l'), (0x1D52A, 'M', u'm'), (0x1D52B, 'M', u'n'), @@ -6440,6 +6555,10 @@ def _seg_61(): (0x1D581, 'M', u'v'), (0x1D582, 'M', u'w'), (0x1D583, 'M', u'x'), + ] + +def _seg_63(): + return [ (0x1D584, 'M', u'y'), (0x1D585, 'M', u'z'), (0x1D586, 'M', u'a'), @@ -6451,10 +6570,6 @@ def _seg_61(): (0x1D58C, 'M', u'g'), (0x1D58D, 'M', u'h'), (0x1D58E, 'M', u'i'), - ] - -def _seg_62(): - return [ (0x1D58F, 'M', u'j'), (0x1D590, 'M', u'k'), (0x1D591, 'M', u'l'), @@ -6544,6 +6659,10 @@ def _seg_62(): (0x1D5E5, 'M', u'r'), (0x1D5E6, 'M', u's'), (0x1D5E7, 'M', u't'), + ] + +def _seg_64(): + return [ (0x1D5E8, 'M', u'u'), (0x1D5E9, 'M', u'v'), (0x1D5EA, 'M', u'w'), @@ -6555,10 +6674,6 @@ def _seg_62(): (0x1D5F0, 'M', u'c'), (0x1D5F1, 'M', u'd'), (0x1D5F2, 'M', u'e'), - ] - -def _seg_63(): - return [ (0x1D5F3, 'M', u'f'), (0x1D5F4, 'M', u'g'), (0x1D5F5, 'M', u'h'), @@ -6648,6 +6763,10 @@ def _seg_63(): (0x1D649, 'M', u'n'), (0x1D64A, 'M', u'o'), (0x1D64B, 'M', u'p'), + ] + +def _seg_65(): + return [ (0x1D64C, 'M', u'q'), (0x1D64D, 'M', u'r'), (0x1D64E, 'M', u's'), @@ -6659,10 +6778,6 @@ def _seg_63(): (0x1D654, 'M', u'y'), (0x1D655, 'M', u'z'), (0x1D656, 'M', u'a'), - ] - -def _seg_64(): - return [ (0x1D657, 'M', u'b'), (0x1D658, 'M', u'c'), (0x1D659, 'M', u'd'), @@ -6752,6 +6867,10 @@ def _seg_64(): (0x1D6AE, 'M', u'η'), (0x1D6AF, 'M', u'θ'), (0x1D6B0, 'M', u'ι'), + ] + +def _seg_66(): + return [ (0x1D6B1, 'M', u'κ'), (0x1D6B2, 'M', u'λ'), (0x1D6B3, 'M', u'μ'), @@ -6763,10 +6882,6 @@ def _seg_64(): (0x1D6B9, 'M', u'θ'), (0x1D6BA, 'M', u'σ'), (0x1D6BB, 'M', u'τ'), - ] - -def _seg_65(): - return [ (0x1D6BC, 'M', u'υ'), (0x1D6BD, 'M', u'φ'), (0x1D6BE, 'M', u'χ'), @@ -6856,6 +6971,10 @@ def _seg_65(): (0x1D714, 'M', u'ω'), (0x1D715, 'M', u'∂'), (0x1D716, 'M', u'ε'), + ] + +def _seg_67(): + return [ (0x1D717, 'M', u'θ'), (0x1D718, 'M', u'κ'), (0x1D719, 'M', u'φ'), @@ -6867,10 +6986,6 @@ def _seg_65(): (0x1D71F, 'M', u'δ'), (0x1D720, 'M', u'ε'), (0x1D721, 'M', u'ζ'), - ] - -def _seg_66(): - return [ (0x1D722, 'M', u'η'), (0x1D723, 'M', u'θ'), (0x1D724, 'M', u'ι'), @@ -6960,6 +7075,10 @@ def _seg_66(): (0x1D779, 'M', u'κ'), (0x1D77A, 'M', u'λ'), (0x1D77B, 'M', u'μ'), + ] + +def _seg_68(): + return [ (0x1D77C, 'M', u'ν'), (0x1D77D, 'M', u'ξ'), (0x1D77E, 'M', u'ο'), @@ -6971,10 +7090,6 @@ def _seg_66(): (0x1D785, 'M', u'φ'), (0x1D786, 'M', u'χ'), (0x1D787, 'M', u'ψ'), - ] - -def _seg_67(): - return [ (0x1D788, 'M', u'ω'), (0x1D789, 'M', u'∂'), (0x1D78A, 'M', u'ε'), @@ -7064,6 +7179,10 @@ def _seg_67(): (0x1D7E1, 'M', u'9'), (0x1D7E2, 'M', u'0'), (0x1D7E3, 'M', u'1'), + ] + +def _seg_69(): + return [ (0x1D7E4, 'M', u'2'), (0x1D7E5, 'M', u'3'), (0x1D7E6, 'M', u'4'), @@ -7075,10 +7194,6 @@ def _seg_67(): (0x1D7EC, 'M', u'0'), (0x1D7ED, 'M', u'1'), (0x1D7EE, 'M', u'2'), - ] - -def _seg_68(): - return [ (0x1D7EF, 'M', u'3'), (0x1D7F0, 'M', u'4'), (0x1D7F1, 'M', u'5'), @@ -7112,6 +7227,18 @@ def _seg_68(): (0x1E025, 'X'), (0x1E026, 'V'), (0x1E02B, 'X'), + (0x1E100, 'V'), + (0x1E12D, 'X'), + (0x1E130, 'V'), + (0x1E13E, 'X'), + (0x1E140, 'V'), + (0x1E14A, 'X'), + (0x1E14E, 'V'), + (0x1E150, 'X'), + (0x1E2C0, 'V'), + (0x1E2FA, 'X'), + (0x1E2FF, 'V'), + (0x1E300, 'X'), (0x1E800, 'V'), (0x1E8C5, 'X'), (0x1E8C7, 'V'), @@ -7151,13 +7278,19 @@ def _seg_68(): (0x1E920, 'M', u'𞥂'), (0x1E921, 'M', u'𞥃'), (0x1E922, 'V'), - (0x1E94B, 'X'), + (0x1E94C, 'X'), (0x1E950, 'V'), (0x1E95A, 'X'), (0x1E95E, 'V'), (0x1E960, 'X'), + ] + +def _seg_70(): + return [ (0x1EC71, 'V'), (0x1ECB5, 'X'), + (0x1ED01, 'V'), + (0x1ED3E, 'X'), (0x1EE00, 'M', u'ا'), (0x1EE01, 'M', u'ب'), (0x1EE02, 'M', u'ج'), @@ -7179,10 +7312,6 @@ def _seg_68(): (0x1EE12, 'M', u'ق'), (0x1EE13, 'M', u'ر'), (0x1EE14, 'M', u'ش'), - ] - -def _seg_69(): - return [ (0x1EE15, 'M', u'ت'), (0x1EE16, 'M', u'ث'), (0x1EE17, 'M', u'خ'), @@ -7258,6 +7387,10 @@ def _seg_69(): (0x1EE68, 'M', u'ط'), (0x1EE69, 'M', u'ي'), (0x1EE6A, 'M', u'ك'), + ] + +def _seg_71(): + return [ (0x1EE6B, 'X'), (0x1EE6C, 'M', u'م'), (0x1EE6D, 'M', u'ن'), @@ -7283,10 +7416,6 @@ def _seg_69(): (0x1EE81, 'M', u'ب'), (0x1EE82, 'M', u'ج'), (0x1EE83, 'M', u'د'), - ] - -def _seg_70(): - return [ (0x1EE84, 'M', u'ه'), (0x1EE85, 'M', u'و'), (0x1EE86, 'M', u'ز'), @@ -7362,10 +7491,13 @@ def _seg_70(): (0x1F106, '3', u'5,'), (0x1F107, '3', u'6,'), (0x1F108, '3', u'7,'), + ] + +def _seg_72(): + return [ (0x1F109, '3', u'8,'), (0x1F10A, '3', u'9,'), (0x1F10B, 'V'), - (0x1F10D, 'X'), (0x1F110, '3', u'(a)'), (0x1F111, '3', u'(b)'), (0x1F112, '3', u'(c)'), @@ -7387,10 +7519,6 @@ def _seg_70(): (0x1F122, '3', u'(s)'), (0x1F123, '3', u'(t)'), (0x1F124, '3', u'(u)'), - ] - -def _seg_71(): - return [ (0x1F125, '3', u'(v)'), (0x1F126, '3', u'(w)'), (0x1F127, '3', u'(x)'), @@ -7437,11 +7565,11 @@ def _seg_71(): (0x1F150, 'V'), (0x1F16A, 'M', u'mc'), (0x1F16B, 'M', u'md'), - (0x1F16C, 'X'), - (0x1F170, 'V'), + (0x1F16C, 'M', u'mr'), + (0x1F16D, 'V'), (0x1F190, 'M', u'dj'), (0x1F191, 'V'), - (0x1F1AD, 'X'), + (0x1F1AE, 'X'), (0x1F1E6, 'V'), (0x1F200, 'M', u'ほか'), (0x1F201, 'M', u'ココ'), @@ -7467,6 +7595,10 @@ def _seg_71(): (0x1F221, 'M', u'終'), (0x1F222, 'M', u'生'), (0x1F223, 'M', u'販'), + ] + +def _seg_73(): + return [ (0x1F224, 'M', u'声'), (0x1F225, 'M', u'吹'), (0x1F226, 'M', u'演'), @@ -7491,10 +7623,6 @@ def _seg_71(): (0x1F239, 'M', u'割'), (0x1F23A, 'M', u'営'), (0x1F23B, 'M', u'配'), - ] - -def _seg_72(): - return [ (0x1F23C, 'X'), (0x1F240, 'M', u'〔本〕'), (0x1F241, 'M', u'〔三〕'), @@ -7512,15 +7640,17 @@ def _seg_72(): (0x1F260, 'V'), (0x1F266, 'X'), (0x1F300, 'V'), - (0x1F6D5, 'X'), + (0x1F6D8, 'X'), (0x1F6E0, 'V'), (0x1F6ED, 'X'), (0x1F6F0, 'V'), - (0x1F6FA, 'X'), + (0x1F6FD, 'X'), (0x1F700, 'V'), (0x1F774, 'X'), (0x1F780, 'V'), (0x1F7D9, 'X'), + (0x1F7E0, 'V'), + (0x1F7EC, 'X'), (0x1F800, 'V'), (0x1F80C, 'X'), (0x1F810, 'V'), @@ -7531,28 +7661,51 @@ def _seg_72(): (0x1F888, 'X'), (0x1F890, 'V'), (0x1F8AE, 'X'), + (0x1F8B0, 'V'), + (0x1F8B2, 'X'), (0x1F900, 'V'), - (0x1F90C, 'X'), - (0x1F910, 'V'), - (0x1F93F, 'X'), - (0x1F940, 'V'), - (0x1F971, 'X'), - (0x1F973, 'V'), - (0x1F977, 'X'), + (0x1F979, 'X'), (0x1F97A, 'V'), - (0x1F97B, 'X'), - (0x1F97C, 'V'), - (0x1F9A3, 'X'), - (0x1F9B0, 'V'), - (0x1F9BA, 'X'), - (0x1F9C0, 'V'), - (0x1F9C3, 'X'), - (0x1F9D0, 'V'), - (0x1FA00, 'X'), + (0x1F9CC, 'X'), + (0x1F9CD, 'V'), + (0x1FA54, 'X'), (0x1FA60, 'V'), (0x1FA6E, 'X'), + (0x1FA70, 'V'), + (0x1FA75, 'X'), + (0x1FA78, 'V'), + (0x1FA7B, 'X'), + (0x1FA80, 'V'), + (0x1FA87, 'X'), + (0x1FA90, 'V'), + (0x1FAA9, 'X'), + (0x1FAB0, 'V'), + (0x1FAB7, 'X'), + (0x1FAC0, 'V'), + (0x1FAC3, 'X'), + (0x1FAD0, 'V'), + (0x1FAD7, 'X'), + (0x1FB00, 'V'), + (0x1FB93, 'X'), + (0x1FB94, 'V'), + (0x1FBCB, 'X'), + (0x1FBF0, 'M', u'0'), + (0x1FBF1, 'M', u'1'), + (0x1FBF2, 'M', u'2'), + (0x1FBF3, 'M', u'3'), + (0x1FBF4, 'M', u'4'), + (0x1FBF5, 'M', u'5'), + (0x1FBF6, 'M', u'6'), + (0x1FBF7, 'M', u'7'), + (0x1FBF8, 'M', u'8'), + (0x1FBF9, 'M', u'9'), + ] + +def _seg_74(): + return [ + (0x1FBFA, 'X'), (0x20000, 'V'), - (0x2A6D7, 'X'), + (0x2A6DE, 'X'), (0x2A700, 'V'), (0x2B735, 'X'), (0x2B740, 'V'), @@ -7595,10 +7748,6 @@ def _seg_72(): (0x2F81F, 'M', u'㓟'), (0x2F820, 'M', u'刻'), (0x2F821, 'M', u'剆'), - ] - -def _seg_73(): - return [ (0x2F822, 'M', u'割'), (0x2F823, 'M', u'剷'), (0x2F824, 'M', u'㔕'), @@ -7654,6 +7803,10 @@ def _seg_73(): (0x2F859, 'M', u'𡓤'), (0x2F85A, 'M', u'売'), (0x2F85B, 'M', u'壷'), + ] + +def _seg_75(): + return [ (0x2F85C, 'M', u'夆'), (0x2F85D, 'M', u'多'), (0x2F85E, 'M', u'夢'), @@ -7699,10 +7852,6 @@ def _seg_73(): (0x2F887, 'M', u'幩'), (0x2F888, 'M', u'㡢'), (0x2F889, 'M', u'𢆃'), - ] - -def _seg_74(): - return [ (0x2F88A, 'M', u'㡼'), (0x2F88B, 'M', u'庰'), (0x2F88C, 'M', u'庳'), @@ -7758,6 +7907,10 @@ def _seg_74(): (0x2F8C0, 'M', u'揅'), (0x2F8C1, 'M', u'掩'), (0x2F8C2, 'M', u'㨮'), + ] + +def _seg_76(): + return [ (0x2F8C3, 'M', u'摩'), (0x2F8C4, 'M', u'摾'), (0x2F8C5, 'M', u'撝'), @@ -7803,10 +7956,6 @@ def _seg_74(): (0x2F8ED, 'M', u'櫛'), (0x2F8EE, 'M', u'㰘'), (0x2F8EF, 'M', u'次'), - ] - -def _seg_75(): - return [ (0x2F8F0, 'M', u'𣢧'), (0x2F8F1, 'M', u'歔'), (0x2F8F2, 'M', u'㱎'), @@ -7862,6 +8011,10 @@ def _seg_75(): (0x2F924, 'M', u'犀'), (0x2F925, 'M', u'犕'), (0x2F926, 'M', u'𤜵'), + ] + +def _seg_77(): + return [ (0x2F927, 'M', u'𤠔'), (0x2F928, 'M', u'獺'), (0x2F929, 'M', u'王'), @@ -7907,10 +8060,6 @@ def _seg_75(): (0x2F953, 'M', u'祖'), (0x2F954, 'M', u'𥚚'), (0x2F955, 'M', u'𥛅'), - ] - -def _seg_76(): - return [ (0x2F956, 'M', u'福'), (0x2F957, 'M', u'秫'), (0x2F958, 'M', u'䄯'), @@ -7966,6 +8115,10 @@ def _seg_76(): (0x2F98B, 'M', u'舁'), (0x2F98C, 'M', u'舄'), (0x2F98D, 'M', u'辞'), + ] + +def _seg_78(): + return [ (0x2F98E, 'M', u'䑫'), (0x2F98F, 'M', u'芑'), (0x2F990, 'M', u'芋'), @@ -8011,10 +8164,6 @@ def _seg_76(): (0x2F9B8, 'M', u'蚈'), (0x2F9B9, 'M', u'蜎'), (0x2F9BA, 'M', u'蛢'), - ] - -def _seg_77(): - return [ (0x2F9BB, 'M', u'蝹'), (0x2F9BC, 'M', u'蜨'), (0x2F9BD, 'M', u'蝫'), @@ -8070,6 +8219,10 @@ def _seg_77(): (0x2F9EF, 'M', u'䦕'), (0x2F9F0, 'M', u'閷'), (0x2F9F1, 'M', u'𨵷'), + ] + +def _seg_79(): + return [ (0x2F9F2, 'M', u'䧦'), (0x2F9F3, 'M', u'雃'), (0x2F9F4, 'M', u'嶲'), @@ -8114,11 +8267,9 @@ def _seg_77(): (0x2FA1C, 'M', u'鼻'), (0x2FA1D, 'M', u'𪘀'), (0x2FA1E, 'X'), + (0x30000, 'V'), + (0x3134B, 'X'), (0xE0100, 'I'), - ] - -def _seg_78(): - return [ (0xE01F0, 'X'), ] @@ -8202,4 +8353,5 @@ uts46data = tuple( + _seg_76() + _seg_77() + _seg_78() + + _seg_79() ) diff --git a/libs/common/requests/__init__.py b/libs/common/requests/__init__.py index bc168ee5..f8f94295 100644 --- a/libs/common/requests/__init__.py +++ b/libs/common/requests/__init__.py @@ -9,14 +9,14 @@ Requests HTTP Library ~~~~~~~~~~~~~~~~~~~~~ -Requests is an HTTP library, written in Python, for human beings. Basic GET -usage: +Requests is an HTTP library, written in Python, for human beings. +Basic GET usage: >>> import requests >>> r = requests.get('https://www.python.org') >>> r.status_code 200 - >>> 'Python is a programming language' in r.content + >>> b'Python is a programming language' in r.content True ... or POST: @@ -27,14 +27,14 @@ usage: { ... "form": { - "key2": "value2", - "key1": "value1" + "key1": "value1", + "key2": "value2" }, ... } The other HTTP methods are supported - see `requests.api`. Full documentation -is at . +is at . :copyright: (c) 2017 by Kenneth Reitz. :license: Apache 2.0, see LICENSE for more details. @@ -57,18 +57,16 @@ def check_compatibility(urllib3_version, chardet_version): # Check urllib3 for compatibility. major, minor, patch = urllib3_version # noqa: F811 major, minor, patch = int(major), int(minor), int(patch) - # urllib3 >= 1.21.1, <= 1.24 + # urllib3 >= 1.21.1, <= 1.26 assert major == 1 assert minor >= 21 - assert minor <= 24 + assert minor <= 26 # Check chardet for compatibility. major, minor, patch = chardet_version.split('.')[:3] major, minor, patch = int(major), int(minor), int(patch) - # chardet >= 3.0.2, < 3.1.0 - assert major == 3 - assert minor < 1 - assert patch >= 2 + # chardet >= 3.0.2, < 5.0.0 + assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0) def _check_cryptography(cryptography_version): @@ -90,14 +88,22 @@ except (AssertionError, ValueError): "version!".format(urllib3.__version__, chardet.__version__), RequestsDependencyWarning) -# Attempt to enable urllib3's SNI support, if possible +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. try: - from urllib3.contrib import pyopenssl - pyopenssl.inject_into_urllib3() + try: + import ssl + except ImportError: + ssl = None - # Check cryptography version - from cryptography import __version__ as cryptography_version - _check_cryptography(cryptography_version) + if not getattr(ssl, "HAS_SNI", False): + from urllib3.contrib import pyopenssl + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + _check_cryptography(cryptography_version) except ImportError: pass diff --git a/libs/common/requests/__version__.py b/libs/common/requests/__version__.py index f5b5d036..1267488d 100644 --- a/libs/common/requests/__version__.py +++ b/libs/common/requests/__version__.py @@ -4,11 +4,11 @@ __title__ = 'requests' __description__ = 'Python HTTP for Humans.' -__url__ = 'http://python-requests.org' -__version__ = '2.21.0' -__build__ = 0x022100 +__url__ = 'https://requests.readthedocs.io' +__version__ = '2.25.1' +__build__ = 0x022501 __author__ = 'Kenneth Reitz' __author_email__ = 'me@kennethreitz.org' __license__ = 'Apache 2.0' -__copyright__ = 'Copyright 2018 Kenneth Reitz' +__copyright__ = 'Copyright 2020 Kenneth Reitz' __cake__ = u'\u2728 \U0001f370 \u2728' diff --git a/libs/common/requests/api.py b/libs/common/requests/api.py index abada96d..e978e203 100644 --- a/libs/common/requests/api.py +++ b/libs/common/requests/api.py @@ -16,10 +16,10 @@ from . import sessions def request(method, url, **kwargs): """Constructs and sends a :class:`Request `. - :param method: method for the new :class:`Request` object. + :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary, list of tuples or bytes to send - in the body of the :class:`Request`. + in the query string for the :class:`Request`. :param data: (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. @@ -50,6 +50,7 @@ def request(method, url, **kwargs): >>> import requests >>> req = requests.request('GET', 'https://httpbin.org/get') + >>> req """ @@ -65,7 +66,7 @@ def get(url, params=None, **kwargs): :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary, list of tuples or bytes to send - in the body of the :class:`Request`. + in the query string for the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. :return: :class:`Response ` object :rtype: requests.Response @@ -92,7 +93,9 @@ def head(url, **kwargs): r"""Sends a HEAD request. :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. + :param \*\*kwargs: Optional arguments that ``request`` takes. If + `allow_redirects` is not provided, it will be set to `False` (as + opposed to the default :meth:`request` behavior). :return: :class:`Response ` object :rtype: requests.Response """ diff --git a/libs/common/requests/auth.py b/libs/common/requests/auth.py index bdde51c7..eeface39 100644 --- a/libs/common/requests/auth.py +++ b/libs/common/requests/auth.py @@ -50,7 +50,7 @@ def _basic_auth_str(username, password): "Non-string passwords will no longer be supported in Requests " "3.0.0. Please convert the object you've passed in ({!r}) to " "a string or bytes object in the near future to avoid " - "problems.".format(password), + "problems.".format(type(password)), category=DeprecationWarning, ) password = str(password) @@ -239,7 +239,7 @@ class HTTPDigestAuth(AuthBase): """ # If response is not 4xx, do not auth - # See https://github.com/requests/requests/issues/3772 + # See https://github.com/psf/requests/issues/3772 if not 400 <= r.status_code < 500: self._thread_local.num_401_calls = 1 return r diff --git a/libs/common/requests/compat.py b/libs/common/requests/compat.py index c44b35ef..5de0769f 100644 --- a/libs/common/requests/compat.py +++ b/libs/common/requests/compat.py @@ -43,6 +43,7 @@ if is_py2: import cookielib from Cookie import Morsel from StringIO import StringIO + # Keep OrderedDict for backwards compatibility. from collections import Callable, Mapping, MutableMapping, OrderedDict @@ -59,6 +60,7 @@ elif is_py3: from http import cookiejar as cookielib from http.cookies import Morsel from io import StringIO + # Keep OrderedDict for backwards compatibility. from collections import OrderedDict from collections.abc import Callable, Mapping, MutableMapping diff --git a/libs/common/requests/exceptions.py b/libs/common/requests/exceptions.py index a80cad80..0e9c820c 100644 --- a/libs/common/requests/exceptions.py +++ b/libs/common/requests/exceptions.py @@ -94,11 +94,11 @@ class ChunkedEncodingError(RequestException): class ContentDecodingError(RequestException, BaseHTTPError): - """Failed to decode response content""" + """Failed to decode response content.""" class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed""" + """The content for this response was already consumed.""" class RetryError(RequestException): @@ -106,21 +106,18 @@ class RetryError(RequestException): class UnrewindableBodyError(RequestException): - """Requests encountered an error when trying to rewind a body""" + """Requests encountered an error when trying to rewind a body.""" # Warnings class RequestsWarning(Warning): """Base warning for Requests.""" - pass class FileModeWarning(RequestsWarning, DeprecationWarning): """A file was opened in text mode, but Requests determined its binary length.""" - pass class RequestsDependencyWarning(RequestsWarning): """An imported dependency doesn't match the expected version range.""" - pass diff --git a/libs/common/requests/models.py b/libs/common/requests/models.py index 62dcd0b7..ec2edc20 100644 --- a/libs/common/requests/models.py +++ b/libs/common/requests/models.py @@ -12,7 +12,7 @@ import sys # Import encoding now, to avoid implicit import later. # Implicit import within threads may cause LookupError when standard library is in a ZIP, -# such as in Embedded Python. See https://github.com/requests/requests/issues/3578. +# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. import encodings.idna from urllib3.fields import RequestField @@ -273,13 +273,16 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): """The fully mutable :class:`PreparedRequest ` object, containing the exact bytes that will be sent to the server. - Generated from either a :class:`Request ` object or manually. + Instances are generated from a :class:`Request ` object, and + should not be instantiated manually; doing so may produce undesirable + effects. Usage:: >>> import requests >>> req = requests.Request('GET', 'https://httpbin.org/get') >>> r = req.prepare() + >>> r >>> s = requests.Session() @@ -358,7 +361,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): #: We're unable to blindly call unicode/str functions #: as this will include the bytestring indicator (b'') #: on python 3.x. - #: https://github.com/requests/requests/pull/2238 + #: https://github.com/psf/requests/pull/2238 if isinstance(url, bytes): url = url.decode('utf8') else: @@ -472,12 +475,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): not isinstance(data, (basestring, list, tuple, Mapping)) ]) - try: - length = super_len(data) - except (TypeError, AttributeError, UnsupportedOperation): - length = None - if is_stream: + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + body = data if getattr(body, 'tell', None) is not None: @@ -608,7 +611,7 @@ class Response(object): #: File-like object representation of response (for advanced usage). #: Use of ``raw`` requires that ``stream=True`` be set on the request. - # This requirement does not apply for use internally to Requests. + #: This requirement does not apply for use internally to Requests. self.raw = None #: Final URL location of Response. @@ -915,7 +918,7 @@ class Response(object): return l def raise_for_status(self): - """Raises stored :class:`HTTPError`, if one occurred.""" + """Raises :class:`HTTPError`, if one occurred.""" http_error_msg = '' if isinstance(self.reason, bytes): diff --git a/libs/common/requests/sessions.py b/libs/common/requests/sessions.py index d73d700f..45ab8a5d 100644 --- a/libs/common/requests/sessions.py +++ b/libs/common/requests/sessions.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """ -requests.session -~~~~~~~~~~~~~~~~ +requests.sessions +~~~~~~~~~~~~~~~~~ This module provides a Session object to manage and persist settings across requests (cookies, auth, proxies). @@ -11,9 +11,10 @@ import os import sys import time from datetime import timedelta +from collections import OrderedDict from .auth import _basic_auth_str -from .compat import cookielib, is_py3, OrderedDict, urljoin, urlparse, Mapping +from .compat import cookielib, is_py3, urljoin, urlparse, Mapping from .cookies import ( cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT @@ -162,7 +163,7 @@ class SessionRedirectMixin(object): resp.raw.read(decode_content=False) if len(resp.history) >= self.max_redirects: - raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) + raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp) # Release the connection back into the pool. resp.close() @@ -170,7 +171,7 @@ class SessionRedirectMixin(object): # Handle redirection without scheme (see: RFC 1808 Section 4) if url.startswith('//'): parsed_rurl = urlparse(resp.url) - url = '%s:%s' % (to_native_string(parsed_rurl.scheme), url) + url = ':'.join([to_native_string(parsed_rurl.scheme), url]) # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) parsed = urlparse(url) @@ -192,19 +193,16 @@ class SessionRedirectMixin(object): self.rebuild_method(prepared_request, resp) - # https://github.com/requests/requests/issues/1084 + # https://github.com/psf/requests/issues/1084 if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): - # https://github.com/requests/requests/issues/3490 + # https://github.com/psf/requests/issues/3490 purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') for header in purged_headers: prepared_request.headers.pop(header, None) prepared_request.body = None headers = prepared_request.headers - try: - del headers['Cookie'] - except KeyError: - pass + headers.pop('Cookie', None) # Extract any cookies sent on the response to the cookiejar # in the new request. Because we've mutated our copied prepared @@ -271,7 +269,6 @@ class SessionRedirectMixin(object): if new_auth is not None: prepared_request.prepare_auth(new_auth) - return def rebuild_proxies(self, prepared_request, proxies): """This method re-evaluates the proxy configuration by considering the @@ -352,13 +349,13 @@ class Session(SessionRedirectMixin): Or as a context manager:: >>> with requests.Session() as s: - >>> s.get('https://httpbin.org/get') + ... s.get('https://httpbin.org/get') """ __attrs__ = [ 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', - 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', + 'cert', 'adapters', 'stream', 'trust_env', 'max_redirects', ] @@ -390,6 +387,13 @@ class Session(SessionRedirectMixin): self.stream = False #: SSL Verification default. + #: Defaults to `True`, requiring requests to verify the TLS certificate at the + #: remote end. + #: If verify is set to `False`, requests will accept any TLS certificate + #: presented by the server, and will ignore hostname mismatches and/or + #: expired certificates, which will make your application vulnerable to + #: man-in-the-middle (MitM) attacks. + #: Only set this to `False` for testing. self.verify = True #: SSL client certificate default, if String, path to ssl client @@ -498,7 +502,12 @@ class Session(SessionRedirectMixin): content. Defaults to ``False``. :param verify: (optional) Either a boolean, in which case it controls whether we verify the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. + to a CA bundle to use. Defaults to ``True``. When set to + ``False``, requests will accept any TLS certificate presented by + the server, and will ignore hostname mismatches and/or expired + certificates, which will make your application vulnerable to + man-in-the-middle (MitM) attacks. Setting verify to ``False`` + may be useful during local development or testing. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :rtype: requests.Response @@ -661,11 +670,13 @@ class Session(SessionRedirectMixin): extract_cookies_to_jar(self.cookies, request, r.raw) - # Redirect resolving generator. - gen = self.resolve_redirects(r, request, **kwargs) - # Resolve redirects if allowed. - history = [resp for resp in gen] if allow_redirects else [] + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] # Shuffle things around if there's history. if history: @@ -728,7 +739,7 @@ class Session(SessionRedirectMixin): return adapter # Nothing matches :-/ - raise InvalidSchema("No connection adapters were found for '%s'" % url) + raise InvalidSchema("No connection adapters were found for {!r}".format(url)) def close(self): """Closes all adapters and as such the session""" diff --git a/libs/common/requests/status_codes.py b/libs/common/requests/status_codes.py index 813e8c4e..d80a7cd4 100644 --- a/libs/common/requests/status_codes.py +++ b/libs/common/requests/status_codes.py @@ -5,12 +5,15 @@ The ``codes`` object defines a mapping from common names for HTTP statuses to their numerical codes, accessible either as attributes or as dictionary items. ->>> requests.codes['temporary_redirect'] -307 ->>> requests.codes.teapot -418 ->>> requests.codes['\o/'] -200 +Example:: + + >>> import requests + >>> requests.codes['temporary_redirect'] + 307 + >>> requests.codes.teapot + 418 + >>> requests.codes['\o/'] + 200 Some codes have multiple names, and both upper- and lower-case versions of the names are allowed. For example, ``codes.ok``, ``codes.OK``, and diff --git a/libs/common/requests/structures.py b/libs/common/requests/structures.py index da930e28..8ee0ba7a 100644 --- a/libs/common/requests/structures.py +++ b/libs/common/requests/structures.py @@ -7,7 +7,9 @@ requests.structures Data structures that power Requests. """ -from .compat import OrderedDict, Mapping, MutableMapping +from collections import OrderedDict + +from .compat import Mapping, MutableMapping class CaseInsensitiveDict(MutableMapping): diff --git a/libs/common/requests/utils.py b/libs/common/requests/utils.py index 8170a8d2..db67938e 100644 --- a/libs/common/requests/utils.py +++ b/libs/common/requests/utils.py @@ -19,6 +19,7 @@ import sys import tempfile import warnings import zipfile +from collections import OrderedDict from .__version__ import __version__ from . import certs @@ -26,7 +27,7 @@ from . import certs from ._internal_utils import to_native_string from .compat import parse_http_list as _parse_list_header from .compat import ( - quote, urlparse, bytes, str, OrderedDict, unquote, getproxies, + quote, urlparse, bytes, str, unquote, getproxies, proxy_bypass, urlunparse, basestring, integer_types, is_py3, proxy_bypass_environment, getproxies_environment, Mapping) from .cookies import cookiejar_from_dict @@ -168,18 +169,24 @@ def super_len(o): def get_netrc_auth(url, raise_errors=False): """Returns the Requests tuple auth for a given url from netrc.""" + netrc_file = os.environ.get('NETRC') + if netrc_file is not None: + netrc_locations = (netrc_file,) + else: + netrc_locations = ('~/{}'.format(f) for f in NETRC_FILES) + try: from netrc import netrc, NetrcParseError netrc_path = None - for f in NETRC_FILES: + for f in netrc_locations: try: - loc = os.path.expanduser('~/{}'.format(f)) + loc = os.path.expanduser(f) except KeyError: # os.path.expanduser can fail when $HOME is undefined and # getpwuid fails. See https://bugs.python.org/issue20164 & - # https://github.com/requests/requests/issues/1846 + # https://github.com/psf/requests/issues/1846 return if os.path.exists(loc): @@ -211,7 +218,7 @@ def get_netrc_auth(url, raise_errors=False): if raise_errors: raise - # AppEngine hackiness. + # App Engine hackiness. except (ImportError, AttributeError): pass @@ -266,6 +273,8 @@ def from_key_val_list(value): >>> from_key_val_list([('key', 'val')]) OrderedDict([('key', 'val')]) >>> from_key_val_list('string') + Traceback (most recent call last): + ... ValueError: cannot encode objects that are not 2-tuples >>> from_key_val_list({'key': 'val'}) OrderedDict([('key', 'val')]) @@ -292,7 +301,9 @@ def to_key_val_list(value): >>> to_key_val_list({'key': 'val'}) [('key', 'val')] >>> to_key_val_list('string') - ValueError: cannot encode objects that are not 2-tuples. + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples :rtype: list """ @@ -492,6 +503,10 @@ def get_encoding_from_headers(headers): if 'text' in content_type: return 'ISO-8859-1' + if 'application/json' in content_type: + # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset + return 'utf-8' + def stream_decode_response_unicode(iterator, r): """Stream decodes a iterator.""" diff --git a/libs/common/urllib3/__init__.py b/libs/common/urllib3/__init__.py index 148a9c31..c6fa3821 100644 --- a/libs/common/urllib3/__init__.py +++ b/libs/common/urllib3/__init__.py @@ -1,49 +1,60 @@ """ -urllib3 - Thread-safe connection pooling and re-using. +Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more """ - from __future__ import absolute_import -import warnings -from .connectionpool import ( - HTTPConnectionPool, - HTTPSConnectionPool, - connection_from_url -) +# Set default logging handler to avoid "No handler found" warnings. +import logging +import warnings +from logging import NullHandler from . import exceptions +from ._version import __version__ +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url from .filepost import encode_multipart_formdata from .poolmanager import PoolManager, ProxyManager, proxy_from_url from .response import HTTPResponse from .util.request import make_headers -from .util.url import get_host -from .util.timeout import Timeout from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import get_host +# === NOTE TO REPACKAGERS AND VENDORS === +# Please delete this block, this logic is only +# for urllib3 being distributed via PyPI. +# See: https://github.com/urllib3/urllib3/issues/2680 +try: + import urllib3_secure_extra # type: ignore # noqa: F401 +except ImportError: + pass +else: + warnings.warn( + "'urllib3[secure]' extra is deprecated and will be removed " + "in a future release of urllib3 2.x. Read more in this issue: " + "https://github.com/urllib3/urllib3/issues/2680", + category=DeprecationWarning, + stacklevel=2, + ) -# Set default logging handler to avoid "No handler found" warnings. -import logging -from logging import NullHandler - -__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' -__license__ = 'MIT' -__version__ = '1.24.1' +__author__ = "Andrey Petrov (andrey.petrov@shazow.net)" +__license__ = "MIT" +__version__ = __version__ __all__ = ( - 'HTTPConnectionPool', - 'HTTPSConnectionPool', - 'PoolManager', - 'ProxyManager', - 'HTTPResponse', - 'Retry', - 'Timeout', - 'add_stderr_logger', - 'connection_from_url', - 'disable_warnings', - 'encode_multipart_formdata', - 'get_host', - 'make_headers', - 'proxy_from_url', + "HTTPConnectionPool", + "HTTPSConnectionPool", + "PoolManager", + "ProxyManager", + "HTTPResponse", + "Retry", + "Timeout", + "add_stderr_logger", + "connection_from_url", + "disable_warnings", + "encode_multipart_formdata", + "get_host", + "make_headers", + "proxy_from_url", ) logging.getLogger(__name__).addHandler(NullHandler()) @@ -60,10 +71,10 @@ def add_stderr_logger(level=logging.DEBUG): # even if urllib3 is vendored within another package. logger = logging.getLogger(__name__) handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) + handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) logger.addHandler(handler) logger.setLevel(level) - logger.debug('Added a stderr logging handler to logger: %s', __name__) + logger.debug("Added a stderr logging handler to logger: %s", __name__) return handler @@ -75,18 +86,17 @@ del NullHandler # shouldn't be: otherwise, it's very hard for users to use most Python # mechanisms to silence them. # SecurityWarning's always go off by default. -warnings.simplefilter('always', exceptions.SecurityWarning, append=True) +warnings.simplefilter("always", exceptions.SecurityWarning, append=True) # SubjectAltNameWarning's should go off once per host -warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) +warnings.simplefilter("default", exceptions.SubjectAltNameWarning, append=True) # InsecurePlatformWarning's don't vary between requests, so we keep it default. -warnings.simplefilter('default', exceptions.InsecurePlatformWarning, - append=True) +warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) # SNIMissingWarnings should go off only once. -warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) +warnings.simplefilter("default", exceptions.SNIMissingWarning, append=True) def disable_warnings(category=exceptions.HTTPWarning): """ Helper for quickly disabling all urllib3 warnings. """ - warnings.simplefilter('ignore', category) + warnings.simplefilter("ignore", category) diff --git a/libs/common/urllib3/_collections.py b/libs/common/urllib3/_collections.py index 34f23811..da9857e9 100644 --- a/libs/common/urllib3/_collections.py +++ b/libs/common/urllib3/_collections.py @@ -1,4 +1,5 @@ from __future__ import absolute_import + try: from collections.abc import Mapping, MutableMapping except ImportError: @@ -6,6 +7,7 @@ except ImportError: try: from threading import RLock except ImportError: # Platform-specific: No threads available + class RLock: def __enter__(self): pass @@ -15,11 +17,12 @@ except ImportError: # Platform-specific: No threads available from collections import OrderedDict + from .exceptions import InvalidHeader -from .packages.six import iterkeys, itervalues, PY3 +from .packages import six +from .packages.six import iterkeys, itervalues - -__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] +__all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] _Null = object() @@ -82,7 +85,9 @@ class RecentlyUsedContainer(MutableMapping): return len(self._container) def __iter__(self): - raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.') + raise NotImplementedError( + "Iteration over this class is unlikely to be threadsafe." + ) def clear(self): with self.lock: @@ -150,7 +155,7 @@ class HTTPHeaderDict(MutableMapping): def __getitem__(self, key): val = self._container[key.lower()] - return ', '.join(val[1:]) + return ", ".join(val[1:]) def __delitem__(self, key): del self._container[key.lower()] @@ -159,17 +164,18 @@ class HTTPHeaderDict(MutableMapping): return key.lower() in self._container def __eq__(self, other): - if not isinstance(other, Mapping) and not hasattr(other, 'keys'): + if not isinstance(other, Mapping) and not hasattr(other, "keys"): return False if not isinstance(other, type(self)): other = type(self)(other) - return (dict((k.lower(), v) for k, v in self.itermerged()) == - dict((k.lower(), v) for k, v in other.itermerged())) + return dict((k.lower(), v) for k, v in self.itermerged()) == dict( + (k.lower(), v) for k, v in other.itermerged() + ) def __ne__(self, other): return not self.__eq__(other) - if not PY3: # Python 2 + if six.PY2: # Python 2 iterkeys = MutableMapping.iterkeys itervalues = MutableMapping.itervalues @@ -184,9 +190,9 @@ class HTTPHeaderDict(MutableMapping): yield vals[0] def pop(self, key, default=__marker): - '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - ''' + """D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + """ # Using the MutableMapping function directly fails due to the private marker. # Using ordinary dict.pop would expose the internal structures. # So let's reinvent the wheel. @@ -228,8 +234,10 @@ class HTTPHeaderDict(MutableMapping): with self.add instead of self.__setitem__ """ if len(args) > 1: - raise TypeError("extend() takes at most 1 positional " - "arguments ({0} given)".format(len(args))) + raise TypeError( + "extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args)) + ) other = args[0] if len(args) >= 1 else () if isinstance(other, HTTPHeaderDict): @@ -295,7 +303,7 @@ class HTTPHeaderDict(MutableMapping): """Iterate over all headers, merging duplicate ones together.""" for key in self: val = self._container[key.lower()] - yield val[0], ', '.join(val[1:]) + yield val[0], ", ".join(val[1:]) def items(self): return list(self.iteritems()) @@ -306,7 +314,7 @@ class HTTPHeaderDict(MutableMapping): # python2.7 does not expose a proper API for exporting multiheaders # efficiently. This function re-reads raw lines from the message # object and extracts the multiheaders properly. - obs_fold_continued_leaders = (' ', '\t') + obs_fold_continued_leaders = (" ", "\t") headers = [] for line in message.headers: @@ -316,14 +324,14 @@ class HTTPHeaderDict(MutableMapping): # in RFC-7230 S3.2.4. This indicates a multiline header, but # there exists no previous header to which we can attach it. raise InvalidHeader( - 'Header continuation with no previous header: %s' % line + "Header continuation with no previous header: %s" % line ) else: key, value = headers[-1] - headers[-1] = (key, value + ' ' + line.strip()) + headers[-1] = (key, value + " " + line.strip()) continue - key, value = line.split(':', 1) + key, value = line.split(":", 1) headers.append((key, value.strip())) return cls(headers) diff --git a/libs/common/urllib3/_version.py b/libs/common/urllib3/_version.py new file mode 100644 index 00000000..308d7f28 --- /dev/null +++ b/libs/common/urllib3/_version.py @@ -0,0 +1,2 @@ +# This file is protected via CODEOWNERS +__version__ = "1.26.13" diff --git a/libs/common/urllib3/connection.py b/libs/common/urllib3/connection.py index 02b36654..10fb36c4 100644 --- a/libs/common/urllib3/connection.py +++ b/libs/common/urllib3/connection.py @@ -1,16 +1,22 @@ from __future__ import absolute_import + import datetime import logging import os +import re import socket -from socket import error as SocketError, timeout as SocketTimeout import warnings +from socket import error as SocketError +from socket import timeout as SocketTimeout + from .packages import six from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection from .packages.six.moves.http_client import HTTPException # noqa: F401 +from .util.proxy import create_proxy_ssl_context try: # Compiled with SSL? import ssl + BaseSSLError = ssl.SSLError except (ImportError, AttributeError): # Platform-specific: No SSL. ssl = None @@ -19,79 +25,81 @@ except (ImportError, AttributeError): # Platform-specific: No SSL. pass -try: # Python 3: - # Not a no-op, we're adding this to the namespace so it can be imported. +try: + # Python 3: not a no-op, we're adding this to the namespace so it can be imported. ConnectionError = ConnectionError -except NameError: # Python 2: +except NameError: + # Python 2 class ConnectionError(Exception): pass +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + BrokenPipeError = BrokenPipeError +except NameError: # Python 2: + + class BrokenPipeError(Exception): + pass + + +from ._collections import HTTPHeaderDict # noqa (historical, removed in v2) +from ._version import __version__ from .exceptions import ( - NewConnectionError, ConnectTimeoutError, + NewConnectionError, SubjectAltNameWarning, SystemTimeWarning, ) -from .packages.ssl_match_hostname import match_hostname, CertificateError - +from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection from .util.ssl_ import ( - resolve_cert_reqs, - resolve_ssl_version, assert_fingerprint, create_urllib3_context, - ssl_wrap_socket + is_ipaddress, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, ) - - -from .util import connection - -from ._collections import HTTPHeaderDict +from .util.ssl_match_hostname import CertificateError, match_hostname log = logging.getLogger(__name__) -port_by_scheme = { - 'http': 80, - 'https': 443, -} +port_by_scheme = {"http": 80, "https": 443} -# When updating RECENT_DATE, move it to within two years of the current date, -# and not less than 6 months ago. -# Example: if Today is 2018-01-01, then RECENT_DATE should be any date on or -# after 2016-01-01 (today - 2 years) AND before 2017-07-01 (today - 6 months) -RECENT_DATE = datetime.date(2017, 6, 30) +# When it comes time to update this value as a part of regular maintenance +# (ie test_recent_date is failing) update it to ~6 months before the current date. +RECENT_DATE = datetime.date(2022, 1, 1) - -class DummyConnection(object): - """Used to detect a failed ConnectionCls import.""" - pass +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") class HTTPConnection(_HTTPConnection, object): """ - Based on httplib.HTTPConnection but provides an extra constructor + Based on :class:`http.client.HTTPConnection` but provides an extra constructor backwards-compatibility layer between older and newer Pythons. Additional keyword parameters are used to configure attributes of the connection. Accepted parameters include: - - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` - - ``source_address``: Set the source address for the current connection. - - ``socket_options``: Set specific options on the underlying socket. If not specified, then - defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling - Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. - For example, if you wish to enable TCP Keep Alive in addition to the defaults, - you might pass:: + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass: - HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), - ] + .. code-block:: python - Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). """ - default_port = port_by_scheme['http'] + default_port = port_by_scheme["http"] #: Disable Nagle's algorithm by default. #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` @@ -100,16 +108,24 @@ class HTTPConnection(_HTTPConnection, object): #: Whether this connection verifies the host's certificate. is_verified = False + #: Whether this proxy connection (if used) verifies the proxy host's + #: certificate. + proxy_is_verified = None + def __init__(self, *args, **kw): - if six.PY3: # Python 3 - kw.pop('strict', None) + if not six.PY2: + kw.pop("strict", None) # Pre-set source_address. - self.source_address = kw.get('source_address') + self.source_address = kw.get("source_address") #: The socket options provided by the user. If no options are #: provided, we use the default options. - self.socket_options = kw.pop('socket_options', self.default_socket_options) + self.socket_options = kw.pop("socket_options", self.default_socket_options) + + # Proxy options provided by the user. + self.proxy = kw.pop("proxy", None) + self.proxy_config = kw.pop("proxy_config", None) _HTTPConnection.__init__(self, *args, **kw) @@ -130,7 +146,7 @@ class HTTPConnection(_HTTPConnection, object): those cases where it's appropriate (i.e., when doing DNS lookup to establish the actual TCP connection across which we're going to send HTTP requests). """ - return self._dns_host.rstrip('.') + return self._dns_host.rstrip(".") @host.setter def host(self, value): @@ -143,35 +159,43 @@ class HTTPConnection(_HTTPConnection, object): self._dns_host = value def _new_conn(self): - """ Establish a socket connection and set nodelay settings on it. + """Establish a socket connection and set nodelay settings on it. :return: New socket connection. """ extra_kw = {} if self.source_address: - extra_kw['source_address'] = self.source_address + extra_kw["source_address"] = self.source_address if self.socket_options: - extra_kw['socket_options'] = self.socket_options + extra_kw["socket_options"] = self.socket_options try: conn = connection.create_connection( - (self._dns_host, self.port), self.timeout, **extra_kw) + (self._dns_host, self.port), self.timeout, **extra_kw + ) - except SocketTimeout as e: + except SocketTimeout: raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout)) + self, + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), + ) except SocketError as e: raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e) + self, "Failed to establish a new connection: %s" % e + ) return conn + def _is_using_tunnel(self): + # Google App Engine's httplib does not define _tunnel_host + return getattr(self, "_tunnel_host", None) + def _prepare_conn(self, conn): self.sock = conn - if self._tunnel_host: + if self._is_using_tunnel(): # TODO: Fix tunnel so it doesn't depend on self.sock state. self._tunnel() # Mark this connection as not reusable @@ -181,24 +205,57 @@ class HTTPConnection(_HTTPConnection, object): conn = self._new_conn() self._prepare_conn(conn) + def putrequest(self, method, url, *args, **kwargs): + """ """ + # Empty docstring because the indentation of CPython's implementation + # is broken but we don't want this method in our documentation. + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + "Method cannot contain non-token characters %r (found at least %r)" + % (method, match.group()) + ) + + return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) + + def putheader(self, header, *values): + """ """ + if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): + _HTTPConnection.putheader(self, header, *values) + elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS: + raise ValueError( + "urllib3.util.SKIP_HEADER only supports '%s'" + % ("', '".join(map(str.title, sorted(SKIPPABLE_HEADERS))),) + ) + + def request(self, method, url, body=None, headers=None): + if headers is None: + headers = {} + else: + # Avoid modifying the headers passed into .request() + headers = headers.copy() + if "user-agent" not in (six.ensure_str(k.lower()) for k in headers): + headers["User-Agent"] = _get_default_user_agent() + super(HTTPConnection, self).request(method, url, body=body, headers=headers) + def request_chunked(self, method, url, body=None, headers=None): """ Alternative to the common request method, which sends the body with chunked encoding and not as one block """ - headers = HTTPHeaderDict(headers if headers is not None else {}) - skip_accept_encoding = 'accept-encoding' in headers - skip_host = 'host' in headers + headers = headers or {} + header_keys = set([six.ensure_str(k.lower()) for k in headers]) + skip_accept_encoding = "accept-encoding" in header_keys + skip_host = "host" in header_keys self.putrequest( - method, - url, - skip_accept_encoding=skip_accept_encoding, - skip_host=skip_host + method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host ) + if "user-agent" not in header_keys: + self.putheader("User-Agent", _get_default_user_agent()) for header, value in headers.items(): self.putheader(header, value) - if 'transfer-encoding' not in headers: - self.putheader('Transfer-Encoding', 'chunked') + if "transfer-encoding" not in header_keys: + self.putheader("Transfer-Encoding", "chunked") self.endheaders() if body is not None: @@ -209,100 +266,104 @@ class HTTPConnection(_HTTPConnection, object): if not chunk: continue if not isinstance(chunk, bytes): - chunk = chunk.encode('utf8') + chunk = chunk.encode("utf8") len_str = hex(len(chunk))[2:] - self.send(len_str.encode('utf-8')) - self.send(b'\r\n') - self.send(chunk) - self.send(b'\r\n') + to_send = bytearray(len_str.encode()) + to_send += b"\r\n" + to_send += chunk + to_send += b"\r\n" + self.send(to_send) # After the if clause, to always have a closed body - self.send(b'0\r\n\r\n') + self.send(b"0\r\n\r\n") class HTTPSConnection(HTTPConnection): - default_port = port_by_scheme['https'] + """ + Many of the parameters to this constructor are passed to the underlying SSL + socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. + """ + default_port = port_by_scheme["https"] + + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ca_cert_data = None ssl_version = None + assert_fingerprint = None + tls_in_tls_required = False - def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - ssl_context=None, server_hostname=None, **kw): + def __init__( + self, + host, + port=None, + key_file=None, + cert_file=None, + key_password=None, + strict=None, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + ssl_context=None, + server_hostname=None, + **kw + ): - HTTPConnection.__init__(self, host, port, strict=strict, - timeout=timeout, **kw) + HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw) self.key_file = key_file self.cert_file = cert_file + self.key_password = key_password self.ssl_context = ssl_context self.server_hostname = server_hostname # Required property for Google AppEngine 1.9.0 which otherwise causes # HTTPS requests to go out as HTTP. (See Issue #356) - self._protocol = 'https' + self._protocol = "https" - def connect(self): - conn = self._new_conn() - self._prepare_conn(conn) - - if self.ssl_context is None: - self.ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(None), - cert_reqs=resolve_cert_reqs(None), - ) - - self.sock = ssl_wrap_socket( - sock=conn, - keyfile=self.key_file, - certfile=self.cert_file, - ssl_context=self.ssl_context, - server_hostname=self.server_hostname - ) - - -class VerifiedHTTPSConnection(HTTPSConnection): - """ - Based on httplib.HTTPSConnection but wraps the socket with - SSL certification. - """ - cert_reqs = None - ca_certs = None - ca_cert_dir = None - ssl_version = None - assert_fingerprint = None - - def set_cert(self, key_file=None, cert_file=None, - cert_reqs=None, ca_certs=None, - assert_hostname=None, assert_fingerprint=None, - ca_cert_dir=None): + def set_cert( + self, + key_file=None, + cert_file=None, + cert_reqs=None, + key_password=None, + ca_certs=None, + assert_hostname=None, + assert_fingerprint=None, + ca_cert_dir=None, + ca_cert_data=None, + ): """ This method should only be called once, before the connection is used. """ - # If cert_reqs is not provided, we can try to guess. If the user gave - # us a cert database, we assume they want to use it: otherwise, if - # they gave us an SSL Context object we should use whatever is set for - # it. + # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also + # have an SSLContext object in which case we'll use its verify_mode. if cert_reqs is None: - if ca_certs or ca_cert_dir: - cert_reqs = 'CERT_REQUIRED' - elif self.ssl_context is not None: + if self.ssl_context is not None: cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) self.key_file = key_file self.cert_file = cert_file self.cert_reqs = cert_reqs + self.key_password = key_password self.assert_hostname = assert_hostname self.assert_fingerprint = assert_fingerprint self.ca_certs = ca_certs and os.path.expanduser(ca_certs) self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data def connect(self): # Add certificate verification - conn = self._new_conn() + self.sock = conn = self._new_conn() hostname = self.host + tls_in_tls = False + + if self._is_using_tunnel(): + if self.tls_in_tls_required: + self.sock = conn = self._connect_tls_proxy(hostname, conn) + tls_in_tls = True - if self._tunnel_host: - self.sock = conn # Calls self._set_hostport(), so self.host is # self._tunnel_host below. self._tunnel() @@ -318,15 +379,19 @@ class VerifiedHTTPSConnection(HTTPSConnection): is_time_off = datetime.date.today() < RECENT_DATE if is_time_off: - warnings.warn(( - 'System time is way off (before {0}). This will probably ' - 'lead to SSL verification errors').format(RECENT_DATE), - SystemTimeWarning + warnings.warn( + ( + "System time is way off (before {0}). This will probably " + "lead to SSL verification errors" + ).format(RECENT_DATE), + SystemTimeWarning, ) # Wrap socket using verification with the root certs in # trusted_root_certs + default_ssl_context = False if self.ssl_context is None: + default_ssl_context = True self.ssl_context = create_urllib3_context( ssl_version=resolve_ssl_version(self.ssl_version), cert_reqs=resolve_cert_reqs(self.cert_reqs), @@ -334,48 +399,150 @@ class VerifiedHTTPSConnection(HTTPSConnection): context = self.ssl_context context.verify_mode = resolve_cert_reqs(self.cert_reqs) + + # Try to load OS default certs if none are given. + # Works well on Windows (requires Python3.4+) + if ( + not self.ca_certs + and not self.ca_cert_dir + and not self.ca_cert_data + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + self.sock = ssl_wrap_socket( sock=conn, keyfile=self.key_file, certfile=self.cert_file, + key_password=self.key_password, ca_certs=self.ca_certs, ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, server_hostname=server_hostname, - ssl_context=context) + ssl_context=context, + tls_in_tls=tls_in_tls, + ) + + # If we're using all defaults and the connection + # is TLSv1 or TLSv1.1 we throw a DeprecationWarning + # for the host. + if ( + default_ssl_context + and self.ssl_version is None + and hasattr(self.sock, "version") + and self.sock.version() in {"TLSv1", "TLSv1.1"} + ): + warnings.warn( + "Negotiating TLSv1/TLSv1.1 by default is deprecated " + "and will be disabled in urllib3 v2.0.0. Connecting to " + "'%s' with '%s' can be enabled by explicitly opting-in " + "with 'ssl_version'" % (self.host, self.sock.version()), + DeprecationWarning, + ) if self.assert_fingerprint: - assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) - elif context.verify_mode != ssl.CERT_NONE \ - and not getattr(context, 'check_hostname', False) \ - and self.assert_hostname is not False: + assert_fingerprint( + self.sock.getpeercert(binary_form=True), self.assert_fingerprint + ) + elif ( + context.verify_mode != ssl.CERT_NONE + and not getattr(context, "check_hostname", False) + and self.assert_hostname is not False + ): # While urllib3 attempts to always turn off hostname matching from # the TLS library, this cannot always be done. So we check whether # the TLS Library still thinks it's matching hostnames. cert = self.sock.getpeercert() - if not cert.get('subjectAltName', ()): - warnings.warn(( - 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' - '`commonName` for now. This feature is being removed by major browsers and ' - 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' - 'for details.)'.format(hostname)), - SubjectAltNameWarning + if not cert.get("subjectAltName", ()): + warnings.warn( + ( + "Certificate for {0} has no `subjectAltName`, falling back to check for a " + "`commonName` for now. This feature is being removed by major browsers and " + "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " + "for details.)".format(hostname) + ), + SubjectAltNameWarning, ) _match_hostname(cert, self.assert_hostname or server_hostname) self.is_verified = ( - context.verify_mode == ssl.CERT_REQUIRED or - self.assert_fingerprint is not None + context.verify_mode == ssl.CERT_REQUIRED + or self.assert_fingerprint is not None ) + def _connect_tls_proxy(self, hostname, conn): + """ + Establish a TLS connection to the proxy using the provided SSL context. + """ + proxy_config = self.proxy_config + ssl_context = proxy_config.ssl_context + if ssl_context: + # If the user provided a proxy context, we assume CA and client + # certificates have already been set + return ssl_wrap_socket( + sock=conn, + server_hostname=hostname, + ssl_context=ssl_context, + ) + + ssl_context = create_proxy_ssl_context( + self.ssl_version, + self.cert_reqs, + self.ca_certs, + self.ca_cert_dir, + self.ca_cert_data, + ) + + # If no cert was provided, use only the default options for server + # certificate validation + socket = ssl_wrap_socket( + sock=conn, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + server_hostname=hostname, + ssl_context=ssl_context, + ) + + if ssl_context.verify_mode != ssl.CERT_NONE and not getattr( + ssl_context, "check_hostname", False + ): + # While urllib3 attempts to always turn off hostname matching from + # the TLS library, this cannot always be done. So we check whether + # the TLS Library still thinks it's matching hostnames. + cert = socket.getpeercert() + if not cert.get("subjectAltName", ()): + warnings.warn( + ( + "Certificate for {0} has no `subjectAltName`, falling back to check for a " + "`commonName` for now. This feature is being removed by major browsers and " + "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " + "for details.)".format(hostname) + ), + SubjectAltNameWarning, + ) + _match_hostname(cert, hostname) + + self.proxy_is_verified = ssl_context.verify_mode == ssl.CERT_REQUIRED + return socket + def _match_hostname(cert, asserted_hostname): + # Our upstream implementation of ssl.match_hostname() + # only applies this normalization to IP addresses so it doesn't + # match DNS SANs so we do the same thing! + stripped_hostname = asserted_hostname.strip("u[]") + if is_ipaddress(stripped_hostname): + asserted_hostname = stripped_hostname + try: match_hostname(cert, asserted_hostname) except CertificateError as e: - log.error( - 'Certificate did not match expected hostname: %s. ' - 'Certificate: %s', asserted_hostname, cert + log.warning( + "Certificate did not match expected hostname: %s. Certificate: %s", + asserted_hostname, + cert, ) # Add cert to exception and reraise so client code can inspect # the cert when catching the exception, if they want to @@ -383,9 +550,18 @@ def _match_hostname(cert, asserted_hostname): raise -if ssl: - # Make a copy for testing. - UnverifiedHTTPSConnection = HTTPSConnection - HTTPSConnection = VerifiedHTTPSConnection -else: - HTTPSConnection = DummyConnection +def _get_default_user_agent(): + return "python-urllib3/%s" % __version__ + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + + pass + + +if not ssl: + HTTPSConnection = DummyConnection # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection diff --git a/libs/common/urllib3/connectionpool.py b/libs/common/urllib3/connectionpool.py index f7a8f193..70873927 100644 --- a/libs/common/urllib3/connectionpool.py +++ b/libs/common/urllib3/connectionpool.py @@ -1,48 +1,54 @@ from __future__ import absolute_import + import errno import logging +import re +import socket import sys import warnings +from socket import error as SocketError +from socket import timeout as SocketTimeout -from socket import error as SocketError, timeout as SocketTimeout -import socket - - +from .connection import ( + BaseSSLError, + BrokenPipeError, + DummyConnection, + HTTPConnection, + HTTPException, + HTTPSConnection, + VerifiedHTTPSConnection, + port_by_scheme, +) from .exceptions import ( ClosedPoolError, - ProtocolError, EmptyPoolError, HeaderParsingError, HostChangedError, + InsecureRequestWarning, LocationValueError, MaxRetryError, + NewConnectionError, + ProtocolError, ProxyError, ReadTimeoutError, SSLError, TimeoutError, - InsecureRequestWarning, - NewConnectionError, ) -from .packages.ssl_match_hostname import CertificateError from .packages import six from .packages.six.moves import queue -from .connection import ( - port_by_scheme, - DummyConnection, - HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, - HTTPException, BaseSSLError, -) from .request import RequestMethods from .response import HTTPResponse - from .util.connection import is_connection_dropped +from .util.proxy import connection_requires_http_tunnel +from .util.queue import LifoQueue from .util.request import set_file_position from .util.response import assert_header_parsing from .util.retry import Retry +from .util.ssl_match_hostname import CertificateError from .util.timeout import Timeout -from .util.url import get_host, Url, NORMALIZABLE_SCHEMES -from .util.queue import LifoQueue - +from .util.url import Url, _encode_target +from .util.url import _normalize_host as normalize_host +from .util.url import get_host, parse_url xrange = six.moves.xrange @@ -56,6 +62,11 @@ class ConnectionPool(object): """ Base class for all connection pools, such as :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. """ scheme = None @@ -65,13 +76,12 @@ class ConnectionPool(object): if not host: raise LocationValueError("No host specified.") - self.host = _ipv6_host(host, self.scheme) + self.host = _normalize_host(host, scheme=self.scheme) self._proxy_host = host.lower() self.port = port def __str__(self): - return '%s(host=%r, port=%r)' % (type(self).__name__, - self.host, self.port) + return "%s(host=%r, port=%r)" % (type(self).__name__, self.host, self.port) def __enter__(self): return self @@ -98,16 +108,16 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param host: Host used for this HTTP Connection (e.g. "localhost"), passed into - :class:`httplib.HTTPConnection`. + :class:`http.client.HTTPConnection`. :param port: Port used for this HTTP Connection (None is equivalent to 80), passed - into :class:`httplib.HTTPConnection`. + into :class:`http.client.HTTPConnection`. :param strict: Causes BadStatusLine to be raised if the status line can't be parsed as a valid HTTP/1.0 or 1.1 status line, passed into - :class:`httplib.HTTPConnection`. + :class:`http.client.HTTPConnection`. .. note:: Only works in Python 2. This parameter is ignored in Python 3. @@ -141,26 +151,36 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param _proxy: Parsed proxy URL, should not be used directly, instead, see - :class:`urllib3.connectionpool.ProxyManager`" + :class:`urllib3.ProxyManager` :param _proxy_headers: A dictionary with proxy headers, should not be used directly, - instead, see :class:`urllib3.connectionpool.ProxyManager`" + instead, see :class:`urllib3.ProxyManager` :param \\**conn_kw: Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, :class:`urllib3.connection.HTTPSConnection` instances. """ - scheme = 'http' + scheme = "http" ConnectionCls = HTTPConnection ResponseCls = HTTPResponse - def __init__(self, host, port=None, strict=False, - timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, - headers=None, retries=None, - _proxy=None, _proxy_headers=None, - **conn_kw): + def __init__( + self, + host, + port=None, + strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, + maxsize=1, + block=False, + headers=None, + retries=None, + _proxy=None, + _proxy_headers=None, + _proxy_config=None, + **conn_kw + ): ConnectionPool.__init__(self, host, port) RequestMethods.__init__(self, headers) @@ -180,6 +200,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): self.proxy = _proxy self.proxy_headers = _proxy_headers or {} + self.proxy_config = _proxy_config # Fill the queue up so that doing get() on it will block properly for _ in xrange(maxsize): @@ -194,19 +215,30 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. # We cannot know if the user has added default socket options, so we cannot replace the # list. - self.conn_kw.setdefault('socket_options', []) + self.conn_kw.setdefault("socket_options", []) + + self.conn_kw["proxy"] = self.proxy + self.conn_kw["proxy_config"] = self.proxy_config def _new_conn(self): """ Return a fresh :class:`HTTPConnection`. """ self.num_connections += 1 - log.debug("Starting new HTTP connection (%d): %s:%s", - self.num_connections, self.host, self.port or "80") + log.debug( + "Starting new HTTP connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "80", + ) - conn = self.ConnectionCls(host=self.host, port=self.port, - timeout=self.timeout.connect_timeout, - strict=self.strict, **self.conn_kw) + conn = self.ConnectionCls( + host=self.host, + port=self.port, + timeout=self.timeout.connect_timeout, + strict=self.strict, + **self.conn_kw + ) return conn def _get_conn(self, timeout=None): @@ -230,18 +262,19 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): except queue.Empty: if self.block: - raise EmptyPoolError(self, - "Pool reached maximum size and no more " - "connections are allowed.") + raise EmptyPoolError( + self, + "Pool reached maximum size and no more connections are allowed.", + ) pass # Oh well, we'll create a new connection then # If this is a persistent connection, check if it got disconnected if conn and is_connection_dropped(conn): log.debug("Resetting dropped connection: %s", self.host) conn.close() - if getattr(conn, 'auto_open', 1) == 0: + if getattr(conn, "auto_open", 1) == 0: # This is a proxied connection that has been mutated by - # httplib._tunnel() and cannot be reused (since it would + # http.client._tunnel() and cannot be reused (since it would # attempt to bypass the proxy) conn = None @@ -270,9 +303,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): except queue.Full: # This should never happen if self.block == True log.warning( - "Connection pool is full, discarding connection: %s", - self.host) - + "Connection pool is full, discarding connection: %s. Connection pool size: %s", + self.host, + self.pool.qsize(), + ) # Connection never got put back into the pool, close it. if conn: conn.close() @@ -288,7 +322,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): pass def _get_timeout(self, timeout): - """ Helper that always returns a :class:`urllib3.util.Timeout` """ + """Helper that always returns a :class:`urllib3.util.Timeout`""" if timeout is _Default: return self.timeout.clone() @@ -303,21 +337,30 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): """Is the error actually a timeout? Will raise a ReadTimeout or pass""" if isinstance(err, SocketTimeout): - raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) # See the above comment about EAGAIN in Python 3. In Python 2 we have # to specifically catch it and throw the timeout error - if hasattr(err, 'errno') and err.errno in _blocking_errnos: - raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + if hasattr(err, "errno") and err.errno in _blocking_errnos: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) # Catch possible read timeouts thrown as SSL errors. If not the # case, rethrow the original. We need to do this because of: # http://bugs.python.org/issue10272 - if 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python < 2.7.4 - raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + if "timed out" in str(err) or "did not complete (read)" in str( + err + ): # Python < 2.7.4 + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % timeout_value + ) - def _make_request(self, conn, method, url, timeout=_Default, chunked=False, - **httplib_request_kw): + def _make_request( + self, conn, method, url, timeout=_Default, chunked=False, **httplib_request_kw + ): """ Perform a request on a given urllib connection object taken from our pool. @@ -346,18 +389,36 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) raise - # conn.request() calls httplib.*.request, not the method in + # conn.request() calls http.client.*.request, not the method in # urllib3.request. It also calls makefile (recv) on the socket. - if chunked: - conn.request_chunked(method, url, **httplib_request_kw) - else: - conn.request(method, url, **httplib_request_kw) + try: + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # We are swallowing BrokenPipeError (errno.EPIPE) since the server is + # legitimately able to close the connection after sending a valid response. + # With this behaviour, the received response is still readable. + except BrokenPipeError: + # Python 3 + pass + except IOError as e: + # Python 2 and macOS/Linux + # EPIPE and ESHUTDOWN are BrokenPipeError on Python 2, and EPROTOTYPE is needed on macOS + # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ + if e.errno not in { + errno.EPIPE, + errno.ESHUTDOWN, + errno.EPROTOTYPE, + }: + raise # Reset the timeout for the recv() on the socket read_timeout = timeout_obj.read_timeout # App Engine doesn't have a sock attr - if getattr(conn, 'sock', None): + if getattr(conn, "sock", None): # In Python 3 socket.py will catch EAGAIN and return None when you # try and read into the file pointer created by http.client, which # instead raises a BadStatusLine exception. Instead of catching @@ -365,7 +426,8 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # timeouts, check for a zero timeout before making the request. if read_timeout == 0: raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % read_timeout) + self, url, "Read timed out. (read timeout=%s)" % read_timeout + ) if read_timeout is Timeout.DEFAULT_TIMEOUT: conn.sock.settimeout(socket.getdefaulttimeout()) else: # None or a value @@ -373,31 +435,45 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # Receive the response from the server try: - try: # Python 2.7, use buffering of HTTP responses + try: + # Python 2.7, use buffering of HTTP responses httplib_response = conn.getresponse(buffering=True) - except TypeError: # Python 3 + except TypeError: + # Python 3 try: httplib_response = conn.getresponse() - except Exception as e: - # Remove the TypeError from the exception chain in Python 3; - # otherwise it looks like a programming error was the cause. + except BaseException as e: + # Remove the TypeError from the exception chain in + # Python 3 (including for exceptions like SystemExit). + # Otherwise it looks like a bug in the code. six.raise_from(e, None) except (SocketTimeout, BaseSSLError, SocketError) as e: self._raise_timeout(err=e, url=url, timeout_value=read_timeout) raise # AppEngine doesn't have a version attr. - http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') - log.debug("%s://%s:%s \"%s %s %s\" %s %s", self.scheme, self.host, self.port, - method, url, http_version, httplib_response.status, - httplib_response.length) + http_version = getattr(conn, "_http_vsn_str", "HTTP/?") + log.debug( + '%s://%s:%s "%s %s %s" %s %s', + self.scheme, + self.host, + self.port, + method, + url, + http_version, + httplib_response.status, + httplib_response.length, + ) try: assert_header_parsing(httplib_response.msg) except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3 log.warning( - 'Failed to parse headers (url=%s): %s', - self._absolute_url(url), hpe, exc_info=True) + "Failed to parse headers (url=%s): %s", + self._absolute_url(url), + hpe, + exc_info=True, + ) return httplib_response @@ -427,13 +503,13 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): Check if the given ``url`` is a member of the same host as this connection pool. """ - if url.startswith('/'): + if url.startswith("/"): return True # TODO: Add optional support for socket.gethostbyname checking. scheme, host, port = get_host(url) - - host = _ipv6_host(host, self.scheme) + if host is not None: + host = _normalize_host(host, scheme=scheme) # Use explicit default port for comparison when none is given if self.port and not port: @@ -443,10 +519,22 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): return (scheme, host, port) == (self.scheme, self.host, self.port) - def urlopen(self, method, url, body=None, headers=None, retries=None, - redirect=True, assert_same_host=True, timeout=_Default, - pool_timeout=None, release_conn=None, chunked=False, - body_pos=None, **response_kw): + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=None, + redirect=True, + assert_same_host=True, + timeout=_Default, + pool_timeout=None, + release_conn=None, + chunked=False, + body_pos=None, + **response_kw + ): """ Get a connection from the pool and perform an HTTP request. This is the lowest level call for making a request, so you'll need to specify all @@ -467,10 +555,12 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param method: HTTP request method (such as GET, POST, PUT, etc.) + :param url: + The URL to perform the request on. + :param body: - Data to send in the request body (useful for creating - POST requests, see HTTPConnectionPool.post_url for - more convenience). + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. :param headers: Dictionary of custom headers to send, such as User-Agent, @@ -500,7 +590,7 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): :param assert_same_host: If ``True``, will make sure that the host of the pool requests is - consistent else will raise HostChangedError. When False, you can + consistent else will raise HostChangedError. When ``False``, you can use the pool on an HTTP proxy and request foreign hosts. :param timeout: @@ -537,6 +627,10 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): Additional parameters are passed to :meth:`urllib3.response.HTTPResponse.from_httplib` """ + + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + if headers is None: headers = self.headers @@ -544,12 +638,18 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): retries = Retry.from_int(retries, redirect=redirect, default=self.retries) if release_conn is None: - release_conn = response_kw.get('preload_content', True) + release_conn = response_kw.get("preload_content", True) # Check host if assert_same_host and not self.is_same_host(url): raise HostChangedError(self, url, retries) + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = six.ensure_str(_encode_target(url)) + else: + url = six.ensure_str(parsed_url.url) + conn = None # Track whether `conn` needs to be released before @@ -560,13 +660,17 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): # # See issue #651 [1] for details. # - # [1] + # [1] release_this_conn = release_conn - # Merge the proxy headers. Only do this in HTTP. We have to copy the - # headers dict so we can safely change it without those changes being - # reflected in anyone else's copy. - if self.scheme == 'http': + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: headers = headers.copy() headers.update(self.proxy_headers) @@ -589,15 +693,22 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): conn.timeout = timeout_obj.connect_timeout - is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) - if is_new_proxy_conn: + is_new_proxy_conn = self.proxy is not None and not getattr( + conn, "sock", None + ) + if is_new_proxy_conn and http_tunnel_required: self._prepare_proxy(conn) # Make the request on the httplib connection object. - httplib_response = self._make_request(conn, method, url, - timeout=timeout_obj, - body=body, headers=headers, - chunked=chunked) + httplib_response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + ) # If we're going to release the connection in ``finally:``, then # the response doesn't need to know about the connection. Otherwise @@ -606,36 +717,76 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): response_conn = conn if not release_conn else None # Pass method to Response for length checking - response_kw['request_method'] = method + response_kw["request_method"] = method # Import httplib's response into our own wrapper object - response = self.ResponseCls.from_httplib(httplib_response, - pool=self, - connection=response_conn, - retries=retries, - **response_kw) + response = self.ResponseCls.from_httplib( + httplib_response, + pool=self, + connection=response_conn, + retries=retries, + **response_kw + ) # Everything went great! clean_exit = True - except queue.Empty: - # Timed out by queue. - raise EmptyPoolError(self, "No pool connections are available.") + except EmptyPoolError: + # Didn't get a connection from the pool, no need to clean up + clean_exit = True + release_this_conn = False + raise - except (TimeoutError, HTTPException, SocketError, ProtocolError, - BaseSSLError, SSLError, CertificateError) as e: + except ( + TimeoutError, + HTTPException, + SocketError, + ProtocolError, + BaseSSLError, + SSLError, + CertificateError, + ) as e: # Discard the connection for these exceptions. It will be # replaced during the next _get_conn() call. clean_exit = False - if isinstance(e, (BaseSSLError, CertificateError)): + + def _is_ssl_error_message_from_http_proxy(ssl_error): + # We're trying to detect the message 'WRONG_VERSION_NUMBER' but + # SSLErrors are kinda all over the place when it comes to the message, + # so we try to cover our bases here! + message = " ".join(re.split("[^a-z]", str(ssl_error).lower())) + return ( + "wrong version number" in message or "unknown protocol" in message + ) + + # Try to detect a common user error with proxies which is to + # set an HTTP proxy to be HTTPS when it should be 'http://' + # (ie {'http': 'http://proxy', 'https': 'https://proxy'}) + # Instead we add a nice error message and point to a URL. + if ( + isinstance(e, BaseSSLError) + and self.proxy + and _is_ssl_error_message_from_http_proxy(e) + and conn.proxy + and conn.proxy.scheme == "https" + ): + e = ProxyError( + "Your proxy appears to only use HTTP and not HTTPS, " + "try changing your proxy URL to be HTTP. See: " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#https-proxy-error-http-proxy", + SSLError(e), + ) + elif isinstance(e, (BaseSSLError, CertificateError)): e = SSLError(e) elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: - e = ProxyError('Cannot connect to proxy.', e) + e = ProxyError("Cannot connect to proxy.", e) elif isinstance(e, (SocketError, HTTPException)): - e = ProtocolError('Connection aborted.', e) + e = ProtocolError("Connection aborted.", e) - retries = retries.increment(method, url, error=e, _pool=self, - _stacktrace=sys.exc_info()[2]) + retries = retries.increment( + method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2] + ) retries.sleep() # Keep track of the error for the retry warning. @@ -658,77 +809,87 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods): if not conn: # Try again - log.warning("Retrying (%r) after connection " - "broken by '%r': %s", retries, err, url) - return self.urlopen(method, url, body, headers, retries, - redirect, assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, body_pos=body_pos, - **response_kw) - - def drain_and_release_conn(response): - try: - # discard any remaining response body, the connection will be - # released back to the pool once the entire response is read - response.read() - except (TimeoutError, HTTPException, SocketError, ProtocolError, - BaseSSLError, SSLError) as e: - pass + log.warning( + "Retrying (%r) after connection broken by '%r': %s", retries, err, url + ) + return self.urlopen( + method, + url, + body, + headers, + retries, + redirect, + assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + **response_kw + ) # Handle redirect? redirect_location = redirect and response.get_redirect_location() if redirect_location: if response.status == 303: - method = 'GET' + method = "GET" try: retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_redirect: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep_for_retry(response) log.debug("Redirecting %s -> %s", url, redirect_location) return self.urlopen( - method, redirect_location, body, headers, - retries=retries, redirect=redirect, + method, + redirect_location, + body, + headers, + retries=retries, + redirect=redirect, assert_same_host=assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, body_pos=body_pos, - **response_kw) + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + **response_kw + ) # Check if we should retry the HTTP response. - has_retry_after = bool(response.getheader('Retry-After')) + has_retry_after = bool(response.headers.get("Retry-After")) if retries.is_retry(method, response.status, has_retry_after): try: retries = retries.increment(method, url, response=response, _pool=self) except MaxRetryError: if retries.raise_on_status: - # Drain and release the connection for this response, since - # we're not returning it to be released manually. - drain_and_release_conn(response) + response.drain_conn() raise return response - # drain and return the connection to the pool before recursing - drain_and_release_conn(response) - + response.drain_conn() retries.sleep(response) log.debug("Retry: %s", url) return self.urlopen( - method, url, body, headers, - retries=retries, redirect=redirect, + method, + url, + body, + headers, + retries=retries, + redirect=redirect, assert_same_host=assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, + timeout=timeout, + pool_timeout=pool_timeout, release_conn=release_conn, - body_pos=body_pos, **response_kw) + chunked=chunked, + body_pos=body_pos, + **response_kw + ) return response @@ -737,42 +898,62 @@ class HTTPSConnectionPool(HTTPConnectionPool): """ Same as :class:`.HTTPConnectionPool`, but HTTPS. - When Python is compiled with the :mod:`ssl` module, then - :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, - instead of :class:`.HTTPSConnection`. - - :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, ``assert_hostname`` and ``host`` in this order to verify connections. If ``assert_hostname`` is False, no verification is done. The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, - ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is - available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` + is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade the connection socket into an SSL socket. """ - scheme = 'https' + scheme = "https" ConnectionCls = HTTPSConnection - def __init__(self, host, port=None, - strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, - block=False, headers=None, retries=None, - _proxy=None, _proxy_headers=None, - key_file=None, cert_file=None, cert_reqs=None, - ca_certs=None, ssl_version=None, - assert_hostname=None, assert_fingerprint=None, - ca_cert_dir=None, **conn_kw): + def __init__( + self, + host, + port=None, + strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, + maxsize=1, + block=False, + headers=None, + retries=None, + _proxy=None, + _proxy_headers=None, + key_file=None, + cert_file=None, + cert_reqs=None, + key_password=None, + ca_certs=None, + ssl_version=None, + assert_hostname=None, + assert_fingerprint=None, + ca_cert_dir=None, + **conn_kw + ): - HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, - block, headers, retries, _proxy, _proxy_headers, - **conn_kw) - - if ca_certs and cert_reqs is None: - cert_reqs = 'CERT_REQUIRED' + HTTPConnectionPool.__init__( + self, + host, + port, + strict, + timeout, + maxsize, + block, + headers, + retries, + _proxy, + _proxy_headers, + **conn_kw + ) self.key_file = key_file self.cert_file = cert_file self.cert_reqs = cert_reqs + self.key_password = key_password self.ca_certs = ca_certs self.ca_cert_dir = ca_cert_dir self.ssl_version = ssl_version @@ -786,35 +967,50 @@ class HTTPSConnectionPool(HTTPConnectionPool): """ if isinstance(conn, VerifiedHTTPSConnection): - conn.set_cert(key_file=self.key_file, - cert_file=self.cert_file, - cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - assert_hostname=self.assert_hostname, - assert_fingerprint=self.assert_fingerprint) + conn.set_cert( + key_file=self.key_file, + key_password=self.key_password, + cert_file=self.cert_file, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ) conn.ssl_version = self.ssl_version return conn def _prepare_proxy(self, conn): """ - Establish tunnel connection early, because otherwise httplib - would improperly set Host: header to proxy's IP:port. + Establishes a tunnel connection through HTTP CONNECT. + + Tunnel connection is established early because otherwise httplib would + improperly set Host: header to proxy's IP:port. """ + conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) + + if self.proxy.scheme == "https": + conn.tls_in_tls_required = True + conn.connect() def _new_conn(self): """ - Return a fresh :class:`httplib.HTTPSConnection`. + Return a fresh :class:`http.client.HTTPSConnection`. """ self.num_connections += 1 - log.debug("Starting new HTTPS connection (%d): %s:%s", - self.num_connections, self.host, self.port or "443") + log.debug( + "Starting new HTTPS connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "443", + ) if not self.ConnectionCls or self.ConnectionCls is DummyConnection: - raise SSLError("Can't connect to HTTPS URL because the SSL " - "module is not available.") + raise SSLError( + "Can't connect to HTTPS URL because the SSL module is not available." + ) actual_host = self.host actual_port = self.port @@ -822,9 +1018,16 @@ class HTTPSConnectionPool(HTTPConnectionPool): actual_host = self.proxy.host actual_port = self.proxy.port - conn = self.ConnectionCls(host=actual_host, port=actual_port, - timeout=self.timeout.connect_timeout, - strict=self.strict, **self.conn_kw) + conn = self.ConnectionCls( + host=actual_host, + port=actual_port, + timeout=self.timeout.connect_timeout, + strict=self.strict, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + **self.conn_kw + ) return self._prepare_conn(conn) @@ -835,16 +1038,30 @@ class HTTPSConnectionPool(HTTPConnectionPool): super(HTTPSConnectionPool, self)._validate_conn(conn) # Force connect early to allow us to validate the connection. - if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` + if not getattr(conn, "sock", None): # AppEngine might not have `.sock` conn.connect() if not conn.is_verified: - warnings.warn(( - 'Unverified HTTPS request is being made. ' - 'Adding certificate verification is strongly advised. See: ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings'), - InsecureRequestWarning) + warnings.warn( + ( + "Unverified HTTPS request is being made to host '%s'. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#ssl-warnings" % conn.host + ), + InsecureRequestWarning, + ) + + if getattr(conn, "proxy_is_verified", None) is False: + warnings.warn( + ( + "Unverified HTTPS connection done to an HTTPS proxy. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#ssl-warnings" + ), + InsecureRequestWarning, + ) def connection_from_url(url, **kw): @@ -869,28 +1086,25 @@ def connection_from_url(url, **kw): """ scheme, host, port = get_host(url) port = port or port_by_scheme.get(scheme, 80) - if scheme == 'https': + if scheme == "https": return HTTPSConnectionPool(host, port=port, **kw) else: return HTTPConnectionPool(host, port=port, **kw) -def _ipv6_host(host, scheme): +def _normalize_host(host, scheme): """ - Process IPv6 address literals + Normalize hosts for comparisons and use with sockets. """ + host = normalize_host(host, scheme) + # httplib doesn't like it when we include brackets in IPv6 addresses # Specifically, if we include brackets but also pass the port then # httplib crazily doubles up the square brackets on the Host header. # Instead, we need to make sure we never pass ``None`` as the port. # However, for backward compatibility reasons we can't actually # *assert* that. See http://bugs.python.org/issue28539 - # - # Also if an IPv6 address literal has a zone identifier, the - # percent sign might be URIencoded, convert it back into ASCII - if host.startswith('[') and host.endswith(']'): - host = host.replace('%25', '%').strip('[]') - if scheme in NORMALIZABLE_SCHEMES: - host = host.lower() + if host.startswith("[") and host.endswith("]"): + host = host[1:-1] return host diff --git a/libs/common/urllib3/contrib/_appengine_environ.py b/libs/common/urllib3/contrib/_appengine_environ.py index f3e00942..8765b907 100644 --- a/libs/common/urllib3/contrib/_appengine_environ.py +++ b/libs/common/urllib3/contrib/_appengine_environ.py @@ -6,25 +6,31 @@ import os def is_appengine(): - return (is_local_appengine() or - is_prod_appengine() or - is_prod_appengine_mvms()) + return is_local_appengine() or is_prod_appengine() def is_appengine_sandbox(): - return is_appengine() and not is_prod_appengine_mvms() + """Reports if the app is running in the first generation sandbox. + + The second generation runtimes are technically still in a sandbox, but it + is much less restrictive, so generally you shouldn't need to check for it. + see https://cloud.google.com/appengine/docs/standard/runtimes + """ + return is_appengine() and os.environ["APPENGINE_RUNTIME"] == "python27" def is_local_appengine(): - return ('APPENGINE_RUNTIME' in os.environ and - 'Development/' in os.environ['SERVER_SOFTWARE']) + return "APPENGINE_RUNTIME" in os.environ and os.environ.get( + "SERVER_SOFTWARE", "" + ).startswith("Development/") def is_prod_appengine(): - return ('APPENGINE_RUNTIME' in os.environ and - 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and - not is_prod_appengine_mvms()) + return "APPENGINE_RUNTIME" in os.environ and os.environ.get( + "SERVER_SOFTWARE", "" + ).startswith("Google App Engine/") def is_prod_appengine_mvms(): - return os.environ.get('GAE_VM', False) == 'true' + """Deprecated.""" + return False diff --git a/libs/common/urllib3/contrib/_securetransport/bindings.py b/libs/common/urllib3/contrib/_securetransport/bindings.py index bcf41c02..264d564d 100644 --- a/libs/common/urllib3/contrib/_securetransport/bindings.py +++ b/libs/common/urllib3/contrib/_securetransport/bindings.py @@ -32,35 +32,60 @@ license and by oscrypto's: from __future__ import absolute_import import platform -from ctypes.util import find_library from ctypes import ( - c_void_p, c_int32, c_char_p, c_size_t, c_byte, c_uint32, c_ulong, c_long, - c_bool + CDLL, + CFUNCTYPE, + POINTER, + c_bool, + c_byte, + c_char_p, + c_int32, + c_long, + c_size_t, + c_uint32, + c_ulong, + c_void_p, ) -from ctypes import CDLL, POINTER, CFUNCTYPE +from ctypes.util import find_library +from ...packages.six import raise_from -security_path = find_library('Security') -if not security_path: - raise ImportError('The library Security could not be found') - - -core_foundation_path = find_library('CoreFoundation') -if not core_foundation_path: - raise ImportError('The library CoreFoundation could not be found') - +if platform.system() != "Darwin": + raise ImportError("Only macOS is supported") version = platform.mac_ver()[0] -version_info = tuple(map(int, version.split('.'))) +version_info = tuple(map(int, version.split("."))) if version_info < (10, 8): raise OSError( - 'Only OS X 10.8 and newer are supported, not %s.%s' % ( - version_info[0], version_info[1] - ) + "Only OS X 10.8 and newer are supported, not %s.%s" + % (version_info[0], version_info[1]) ) -Security = CDLL(security_path, use_errno=True) -CoreFoundation = CDLL(core_foundation_path, use_errno=True) + +def load_cdll(name, macos10_16_path): + """Loads a CDLL by name, falling back to known path on 10.16+""" + try: + # Big Sur is technically 11 but we use 10.16 due to the Big Sur + # beta being labeled as 10.16. + if version_info >= (10, 16): + path = macos10_16_path + else: + path = find_library(name) + if not path: + raise OSError # Caught and reraised as 'ImportError' + return CDLL(path, use_errno=True) + except OSError: + raise_from(ImportError("The library %s failed to load" % name), None) + + +Security = load_cdll( + "Security", "/System/Library/Frameworks/Security.framework/Security" +) +CoreFoundation = load_cdll( + "CoreFoundation", + "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", +) + Boolean = c_bool CFIndex = c_long @@ -129,27 +154,19 @@ try: Security.SecKeyGetTypeID.argtypes = [] Security.SecKeyGetTypeID.restype = CFTypeID - Security.SecCertificateCreateWithData.argtypes = [ - CFAllocatorRef, - CFDataRef - ] + Security.SecCertificateCreateWithData.argtypes = [CFAllocatorRef, CFDataRef] Security.SecCertificateCreateWithData.restype = SecCertificateRef - Security.SecCertificateCopyData.argtypes = [ - SecCertificateRef - ] + Security.SecCertificateCopyData.argtypes = [SecCertificateRef] Security.SecCertificateCopyData.restype = CFDataRef - Security.SecCopyErrorMessageString.argtypes = [ - OSStatus, - c_void_p - ] + Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] Security.SecCopyErrorMessageString.restype = CFStringRef Security.SecIdentityCreateWithCertificate.argtypes = [ CFTypeRef, SecCertificateRef, - POINTER(SecIdentityRef) + POINTER(SecIdentityRef), ] Security.SecIdentityCreateWithCertificate.restype = OSStatus @@ -159,201 +176,133 @@ try: c_void_p, Boolean, c_void_p, - POINTER(SecKeychainRef) + POINTER(SecKeychainRef), ] Security.SecKeychainCreate.restype = OSStatus - Security.SecKeychainDelete.argtypes = [ - SecKeychainRef - ] + Security.SecKeychainDelete.argtypes = [SecKeychainRef] Security.SecKeychainDelete.restype = OSStatus Security.SecPKCS12Import.argtypes = [ CFDataRef, CFDictionaryRef, - POINTER(CFArrayRef) + POINTER(CFArrayRef), ] Security.SecPKCS12Import.restype = OSStatus SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, c_void_p, POINTER(c_size_t)) - SSLWriteFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t)) + SSLWriteFunc = CFUNCTYPE( + OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t) + ) - Security.SSLSetIOFuncs.argtypes = [ - SSLContextRef, - SSLReadFunc, - SSLWriteFunc - ] + Security.SSLSetIOFuncs.argtypes = [SSLContextRef, SSLReadFunc, SSLWriteFunc] Security.SSLSetIOFuncs.restype = OSStatus - Security.SSLSetPeerID.argtypes = [ - SSLContextRef, - c_char_p, - c_size_t - ] + Security.SSLSetPeerID.argtypes = [SSLContextRef, c_char_p, c_size_t] Security.SSLSetPeerID.restype = OSStatus - Security.SSLSetCertificate.argtypes = [ - SSLContextRef, - CFArrayRef - ] + Security.SSLSetCertificate.argtypes = [SSLContextRef, CFArrayRef] Security.SSLSetCertificate.restype = OSStatus - Security.SSLSetCertificateAuthorities.argtypes = [ - SSLContextRef, - CFTypeRef, - Boolean - ] + Security.SSLSetCertificateAuthorities.argtypes = [SSLContextRef, CFTypeRef, Boolean] Security.SSLSetCertificateAuthorities.restype = OSStatus - Security.SSLSetConnection.argtypes = [ - SSLContextRef, - SSLConnectionRef - ] + Security.SSLSetConnection.argtypes = [SSLContextRef, SSLConnectionRef] Security.SSLSetConnection.restype = OSStatus - Security.SSLSetPeerDomainName.argtypes = [ - SSLContextRef, - c_char_p, - c_size_t - ] + Security.SSLSetPeerDomainName.argtypes = [SSLContextRef, c_char_p, c_size_t] Security.SSLSetPeerDomainName.restype = OSStatus - Security.SSLHandshake.argtypes = [ - SSLContextRef - ] + Security.SSLHandshake.argtypes = [SSLContextRef] Security.SSLHandshake.restype = OSStatus - Security.SSLRead.argtypes = [ - SSLContextRef, - c_char_p, - c_size_t, - POINTER(c_size_t) - ] + Security.SSLRead.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] Security.SSLRead.restype = OSStatus - Security.SSLWrite.argtypes = [ - SSLContextRef, - c_char_p, - c_size_t, - POINTER(c_size_t) - ] + Security.SSLWrite.argtypes = [SSLContextRef, c_char_p, c_size_t, POINTER(c_size_t)] Security.SSLWrite.restype = OSStatus - Security.SSLClose.argtypes = [ - SSLContextRef - ] + Security.SSLClose.argtypes = [SSLContextRef] Security.SSLClose.restype = OSStatus - Security.SSLGetNumberSupportedCiphers.argtypes = [ - SSLContextRef, - POINTER(c_size_t) - ] + Security.SSLGetNumberSupportedCiphers.argtypes = [SSLContextRef, POINTER(c_size_t)] Security.SSLGetNumberSupportedCiphers.restype = OSStatus Security.SSLGetSupportedCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), - POINTER(c_size_t) + POINTER(c_size_t), ] Security.SSLGetSupportedCiphers.restype = OSStatus Security.SSLSetEnabledCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), - c_size_t + c_size_t, ] Security.SSLSetEnabledCiphers.restype = OSStatus - Security.SSLGetNumberEnabledCiphers.argtype = [ - SSLContextRef, - POINTER(c_size_t) - ] + Security.SSLGetNumberEnabledCiphers.argtype = [SSLContextRef, POINTER(c_size_t)] Security.SSLGetNumberEnabledCiphers.restype = OSStatus Security.SSLGetEnabledCiphers.argtypes = [ SSLContextRef, POINTER(SSLCipherSuite), - POINTER(c_size_t) + POINTER(c_size_t), ] Security.SSLGetEnabledCiphers.restype = OSStatus - Security.SSLGetNegotiatedCipher.argtypes = [ - SSLContextRef, - POINTER(SSLCipherSuite) - ] + Security.SSLGetNegotiatedCipher.argtypes = [SSLContextRef, POINTER(SSLCipherSuite)] Security.SSLGetNegotiatedCipher.restype = OSStatus Security.SSLGetNegotiatedProtocolVersion.argtypes = [ SSLContextRef, - POINTER(SSLProtocol) + POINTER(SSLProtocol), ] Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus - Security.SSLCopyPeerTrust.argtypes = [ - SSLContextRef, - POINTER(SecTrustRef) - ] + Security.SSLCopyPeerTrust.argtypes = [SSLContextRef, POINTER(SecTrustRef)] Security.SSLCopyPeerTrust.restype = OSStatus - Security.SecTrustSetAnchorCertificates.argtypes = [ - SecTrustRef, - CFArrayRef - ] + Security.SecTrustSetAnchorCertificates.argtypes = [SecTrustRef, CFArrayRef] Security.SecTrustSetAnchorCertificates.restype = OSStatus - Security.SecTrustSetAnchorCertificatesOnly.argstypes = [ - SecTrustRef, - Boolean - ] + Security.SecTrustSetAnchorCertificatesOnly.argstypes = [SecTrustRef, Boolean] Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus - Security.SecTrustEvaluate.argtypes = [ - SecTrustRef, - POINTER(SecTrustResultType) - ] + Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)] Security.SecTrustEvaluate.restype = OSStatus - Security.SecTrustGetCertificateCount.argtypes = [ - SecTrustRef - ] + Security.SecTrustGetCertificateCount.argtypes = [SecTrustRef] Security.SecTrustGetCertificateCount.restype = CFIndex - Security.SecTrustGetCertificateAtIndex.argtypes = [ - SecTrustRef, - CFIndex - ] + Security.SecTrustGetCertificateAtIndex.argtypes = [SecTrustRef, CFIndex] Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef Security.SSLCreateContext.argtypes = [ CFAllocatorRef, SSLProtocolSide, - SSLConnectionType + SSLConnectionType, ] Security.SSLCreateContext.restype = SSLContextRef - Security.SSLSetSessionOption.argtypes = [ - SSLContextRef, - SSLSessionOption, - Boolean - ] + Security.SSLSetSessionOption.argtypes = [SSLContextRef, SSLSessionOption, Boolean] Security.SSLSetSessionOption.restype = OSStatus - Security.SSLSetProtocolVersionMin.argtypes = [ - SSLContextRef, - SSLProtocol - ] + Security.SSLSetProtocolVersionMin.argtypes = [SSLContextRef, SSLProtocol] Security.SSLSetProtocolVersionMin.restype = OSStatus - Security.SSLSetProtocolVersionMax.argtypes = [ - SSLContextRef, - SSLProtocol - ] + Security.SSLSetProtocolVersionMax.argtypes = [SSLContextRef, SSLProtocol] Security.SSLSetProtocolVersionMax.restype = OSStatus - Security.SecCopyErrorMessageString.argtypes = [ - OSStatus, - c_void_p - ] + try: + Security.SSLSetALPNProtocols.argtypes = [SSLContextRef, CFArrayRef] + Security.SSLSetALPNProtocols.restype = OSStatus + except AttributeError: + # Supported only in 10.12+ + pass + + Security.SecCopyErrorMessageString.argtypes = [OSStatus, c_void_p] Security.SecCopyErrorMessageString.restype = CFStringRef Security.SSLReadFunc = SSLReadFunc @@ -369,64 +318,47 @@ try: Security.OSStatus = OSStatus Security.kSecImportExportPassphrase = CFStringRef.in_dll( - Security, 'kSecImportExportPassphrase' + Security, "kSecImportExportPassphrase" ) Security.kSecImportItemIdentity = CFStringRef.in_dll( - Security, 'kSecImportItemIdentity' + Security, "kSecImportItemIdentity" ) # CoreFoundation time! - CoreFoundation.CFRetain.argtypes = [ - CFTypeRef - ] + CoreFoundation.CFRetain.argtypes = [CFTypeRef] CoreFoundation.CFRetain.restype = CFTypeRef - CoreFoundation.CFRelease.argtypes = [ - CFTypeRef - ] + CoreFoundation.CFRelease.argtypes = [CFTypeRef] CoreFoundation.CFRelease.restype = None - CoreFoundation.CFGetTypeID.argtypes = [ - CFTypeRef - ] + CoreFoundation.CFGetTypeID.argtypes = [CFTypeRef] CoreFoundation.CFGetTypeID.restype = CFTypeID CoreFoundation.CFStringCreateWithCString.argtypes = [ CFAllocatorRef, c_char_p, - CFStringEncoding + CFStringEncoding, ] CoreFoundation.CFStringCreateWithCString.restype = CFStringRef - CoreFoundation.CFStringGetCStringPtr.argtypes = [ - CFStringRef, - CFStringEncoding - ] + CoreFoundation.CFStringGetCStringPtr.argtypes = [CFStringRef, CFStringEncoding] CoreFoundation.CFStringGetCStringPtr.restype = c_char_p CoreFoundation.CFStringGetCString.argtypes = [ CFStringRef, c_char_p, CFIndex, - CFStringEncoding + CFStringEncoding, ] CoreFoundation.CFStringGetCString.restype = c_bool - CoreFoundation.CFDataCreate.argtypes = [ - CFAllocatorRef, - c_char_p, - CFIndex - ] + CoreFoundation.CFDataCreate.argtypes = [CFAllocatorRef, c_char_p, CFIndex] CoreFoundation.CFDataCreate.restype = CFDataRef - CoreFoundation.CFDataGetLength.argtypes = [ - CFDataRef - ] + CoreFoundation.CFDataGetLength.argtypes = [CFDataRef] CoreFoundation.CFDataGetLength.restype = CFIndex - CoreFoundation.CFDataGetBytePtr.argtypes = [ - CFDataRef - ] + CoreFoundation.CFDataGetBytePtr.argtypes = [CFDataRef] CoreFoundation.CFDataGetBytePtr.restype = c_void_p CoreFoundation.CFDictionaryCreate.argtypes = [ @@ -435,14 +367,11 @@ try: POINTER(CFTypeRef), CFIndex, CFDictionaryKeyCallBacks, - CFDictionaryValueCallBacks + CFDictionaryValueCallBacks, ] CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef - CoreFoundation.CFDictionaryGetValue.argtypes = [ - CFDictionaryRef, - CFTypeRef - ] + CoreFoundation.CFDictionaryGetValue.argtypes = [CFDictionaryRef, CFTypeRef] CoreFoundation.CFDictionaryGetValue.restype = CFTypeRef CoreFoundation.CFArrayCreate.argtypes = [ @@ -456,36 +385,30 @@ try: CoreFoundation.CFArrayCreateMutable.argtypes = [ CFAllocatorRef, CFIndex, - CFArrayCallBacks + CFArrayCallBacks, ] CoreFoundation.CFArrayCreateMutable.restype = CFMutableArrayRef - CoreFoundation.CFArrayAppendValue.argtypes = [ - CFMutableArrayRef, - c_void_p - ] + CoreFoundation.CFArrayAppendValue.argtypes = [CFMutableArrayRef, c_void_p] CoreFoundation.CFArrayAppendValue.restype = None - CoreFoundation.CFArrayGetCount.argtypes = [ - CFArrayRef - ] + CoreFoundation.CFArrayGetCount.argtypes = [CFArrayRef] CoreFoundation.CFArrayGetCount.restype = CFIndex - CoreFoundation.CFArrayGetValueAtIndex.argtypes = [ - CFArrayRef, - CFIndex - ] + CoreFoundation.CFArrayGetValueAtIndex.argtypes = [CFArrayRef, CFIndex] CoreFoundation.CFArrayGetValueAtIndex.restype = c_void_p CoreFoundation.kCFAllocatorDefault = CFAllocatorRef.in_dll( - CoreFoundation, 'kCFAllocatorDefault' + CoreFoundation, "kCFAllocatorDefault" + ) + CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll( + CoreFoundation, "kCFTypeArrayCallBacks" ) - CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeArrayCallBacks') CoreFoundation.kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll( - CoreFoundation, 'kCFTypeDictionaryKeyCallBacks' + CoreFoundation, "kCFTypeDictionaryKeyCallBacks" ) CoreFoundation.kCFTypeDictionaryValueCallBacks = c_void_p.in_dll( - CoreFoundation, 'kCFTypeDictionaryValueCallBacks' + CoreFoundation, "kCFTypeDictionaryValueCallBacks" ) CoreFoundation.CFTypeRef = CFTypeRef @@ -494,7 +417,7 @@ try: CoreFoundation.CFDictionaryRef = CFDictionaryRef except (AttributeError): - raise ImportError('Error initializing ctypes') + raise ImportError("Error initializing ctypes") class CFConst(object): @@ -502,6 +425,7 @@ class CFConst(object): A class object that acts as essentially a namespace for CoreFoundation constants. """ + kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) @@ -509,6 +433,7 @@ class SecurityConst(object): """ A class object that acts as essentially a namespace for Security constants. """ + kSSLSessionOptionBreakOnServerAuth = 0 kSSLProtocol2 = 1 @@ -516,6 +441,9 @@ class SecurityConst(object): kTLSProtocol1 = 4 kTLSProtocol11 = 7 kTLSProtocol12 = 8 + # SecureTransport does not support TLS 1.3 even if there's a constant for it + kTLSProtocol13 = 10 + kTLSProtocolMaxSupported = 999 kSSLClientSide = 1 kSSLStreamType = 0 @@ -558,30 +486,27 @@ class SecurityConst(object): errSecInvalidTrustSettings = -25262 # Cipher suites. We only pick the ones our default cipher string allows. + # Source: https://developer.apple.com/documentation/security/1550981-ssl_cipher_suite_values TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F - TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3 + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9 + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F - TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B - TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 - TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 - TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040 TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032 TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D @@ -590,4 +515,5 @@ class SecurityConst(object): TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F TLS_AES_128_GCM_SHA256 = 0x1301 TLS_AES_256_GCM_SHA384 = 0x1302 - TLS_CHACHA20_POLY1305_SHA256 = 0x1303 + TLS_AES_128_CCM_8_SHA256 = 0x1305 + TLS_AES_128_CCM_SHA256 = 0x1304 diff --git a/libs/common/urllib3/contrib/_securetransport/low_level.py b/libs/common/urllib3/contrib/_securetransport/low_level.py index b13cd9e7..fa0b245d 100644 --- a/libs/common/urllib3/contrib/_securetransport/low_level.py +++ b/libs/common/urllib3/contrib/_securetransport/low_level.py @@ -10,13 +10,13 @@ appropriate and useful assistance to the higher-level code. import base64 import ctypes import itertools -import re import os +import re import ssl +import struct import tempfile -from .bindings import Security, CoreFoundation, CFConst - +from .bindings import CFConst, CoreFoundation, Security # This regular expression is used to grab PEM data out of a PEM bundle. _PEM_CERTS_RE = re.compile( @@ -56,6 +56,51 @@ def _cf_dictionary_from_tuples(tuples): ) +def _cfstr(py_bstr): + """ + Given a Python binary data, create a CFString. + The string must be CFReleased by the caller. + """ + c_str = ctypes.c_char_p(py_bstr) + cf_str = CoreFoundation.CFStringCreateWithCString( + CoreFoundation.kCFAllocatorDefault, + c_str, + CFConst.kCFStringEncodingUTF8, + ) + return cf_str + + +def _create_cfstring_array(lst): + """ + Given a list of Python binary data, create an associated CFMutableArray. + The array must be CFReleased by the caller. + + Raises an ssl.SSLError on failure. + """ + cf_arr = None + try: + cf_arr = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + if not cf_arr: + raise MemoryError("Unable to allocate memory!") + for item in lst: + cf_str = _cfstr(item) + if not cf_str: + raise MemoryError("Unable to allocate memory!") + try: + CoreFoundation.CFArrayAppendValue(cf_arr, cf_str) + finally: + CoreFoundation.CFRelease(cf_str) + except BaseException as e: + if cf_arr: + CoreFoundation.CFRelease(cf_arr) + raise ssl.SSLError("Unable to allocate array: %s" % (e,)) + return cf_arr + + def _cf_string_to_unicode(value): """ Creates a Unicode string from a CFString object. Used entirely for error @@ -66,22 +111,18 @@ def _cf_string_to_unicode(value): value_as_void_p = ctypes.cast(value, ctypes.POINTER(ctypes.c_void_p)) string = CoreFoundation.CFStringGetCStringPtr( - value_as_void_p, - CFConst.kCFStringEncodingUTF8 + value_as_void_p, CFConst.kCFStringEncodingUTF8 ) if string is None: buffer = ctypes.create_string_buffer(1024) result = CoreFoundation.CFStringGetCString( - value_as_void_p, - buffer, - 1024, - CFConst.kCFStringEncodingUTF8 + value_as_void_p, buffer, 1024, CFConst.kCFStringEncodingUTF8 ) if not result: - raise OSError('Error copying C string from CFStringRef') + raise OSError("Error copying C string from CFStringRef") string = buffer.value if string is not None: - string = string.decode('utf-8') + string = string.decode("utf-8") return string @@ -97,8 +138,8 @@ def _assert_no_error(error, exception_class=None): output = _cf_string_to_unicode(cf_error_string) CoreFoundation.CFRelease(cf_error_string) - if output is None or output == u'': - output = u'OSStatus %s' % error + if output is None or output == u"": + output = u"OSStatus %s" % error if exception_class is None: exception_class = ssl.SSLError @@ -115,8 +156,7 @@ def _cert_array_from_pem(pem_bundle): pem_bundle = pem_bundle.replace(b"\r\n", b"\n") der_certs = [ - base64.b64decode(match.group(1)) - for match in _PEM_CERTS_RE.finditer(pem_bundle) + base64.b64decode(match.group(1)) for match in _PEM_CERTS_RE.finditer(pem_bundle) ] if not der_certs: raise ssl.SSLError("No root certificates specified") @@ -124,7 +164,7 @@ def _cert_array_from_pem(pem_bundle): cert_array = CoreFoundation.CFArrayCreateMutable( CoreFoundation.kCFAllocatorDefault, 0, - ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks) + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), ) if not cert_array: raise ssl.SSLError("Unable to allocate memory!") @@ -148,6 +188,7 @@ def _cert_array_from_pem(pem_bundle): # We only want to do that if an error occurs: otherwise, the caller # should free. CoreFoundation.CFRelease(cert_array) + raise return cert_array @@ -186,21 +227,16 @@ def _temporary_keychain(): # some random bytes to password-protect the keychain we're creating, so we # ask for 40 random bytes. random_bytes = os.urandom(40) - filename = base64.b16encode(random_bytes[:8]).decode('utf-8') + filename = base64.b16encode(random_bytes[:8]).decode("utf-8") password = base64.b16encode(random_bytes[8:]) # Must be valid UTF-8 tempdirectory = tempfile.mkdtemp() - keychain_path = os.path.join(tempdirectory, filename).encode('utf-8') + keychain_path = os.path.join(tempdirectory, filename).encode("utf-8") # We now want to create the keychain itself. keychain = Security.SecKeychainRef() status = Security.SecKeychainCreate( - keychain_path, - len(password), - password, - False, - None, - ctypes.byref(keychain) + keychain_path, len(password), password, False, None, ctypes.byref(keychain) ) _assert_no_error(status) @@ -219,14 +255,12 @@ def _load_items_from_file(keychain, path): identities = [] result_array = None - with open(path, 'rb') as f: + with open(path, "rb") as f: raw_filedata = f.read() try: filedata = CoreFoundation.CFDataCreate( - CoreFoundation.kCFAllocatorDefault, - raw_filedata, - len(raw_filedata) + CoreFoundation.kCFAllocatorDefault, raw_filedata, len(raw_filedata) ) result_array = CoreFoundation.CFArrayRef() result = Security.SecItemImport( @@ -237,7 +271,7 @@ def _load_items_from_file(keychain, path): 0, # import flags None, # key params, can include passphrase in the future keychain, # The keychain to insert into - ctypes.byref(result_array) # Results + ctypes.byref(result_array), # Results ) _assert_no_error(result) @@ -247,9 +281,7 @@ def _load_items_from_file(keychain, path): # keychain already has them! result_count = CoreFoundation.CFArrayGetCount(result_array) for index in range(result_count): - item = CoreFoundation.CFArrayGetValueAtIndex( - result_array, index - ) + item = CoreFoundation.CFArrayGetValueAtIndex(result_array, index) item = ctypes.cast(item, CoreFoundation.CFTypeRef) if _is_cert(item): @@ -307,9 +339,7 @@ def _load_client_cert_chain(keychain, *paths): try: for file_path in paths: - new_identities, new_certs = _load_items_from_file( - keychain, file_path - ) + new_identities, new_certs = _load_items_from_file(keychain, file_path) identities.extend(new_identities) certificates.extend(new_certs) @@ -318,9 +348,7 @@ def _load_client_cert_chain(keychain, *paths): if not identities: new_identity = Security.SecIdentityRef() status = Security.SecIdentityCreateWithCertificate( - keychain, - certificates[0], - ctypes.byref(new_identity) + keychain, certificates[0], ctypes.byref(new_identity) ) _assert_no_error(status) identities.append(new_identity) @@ -344,3 +372,26 @@ def _load_client_cert_chain(keychain, *paths): finally: for obj in itertools.chain(identities, certificates): CoreFoundation.CFRelease(obj) + + +TLS_PROTOCOL_VERSIONS = { + "SSLv2": (0, 2), + "SSLv3": (3, 0), + "TLSv1": (3, 1), + "TLSv1.1": (3, 2), + "TLSv1.2": (3, 3), +} + + +def _build_tls_unknown_ca_alert(version): + """ + Builds a TLS alert record for an unknown CA. + """ + ver_maj, ver_min = TLS_PROTOCOL_VERSIONS[version] + severity_fatal = 0x02 + description_unknown_ca = 0x30 + msg = struct.pack(">BB", severity_fatal, description_unknown_ca) + msg_len = len(msg) + record_type_alert = 0x15 + record = struct.pack(">BBBH", record_type_alert, ver_maj, ver_min, msg_len) + msg + return record diff --git a/libs/common/urllib3/contrib/appengine.py b/libs/common/urllib3/contrib/appengine.py index 2952f114..f91bdd6e 100644 --- a/libs/common/urllib3/contrib/appengine.py +++ b/libs/common/urllib3/contrib/appengine.py @@ -39,24 +39,24 @@ urllib3 on Google App Engine: """ from __future__ import absolute_import + import io import logging import warnings -from ..packages.six.moves.urllib.parse import urljoin from ..exceptions import ( HTTPError, HTTPWarning, MaxRetryError, ProtocolError, + SSLError, TimeoutError, - SSLError ) - +from ..packages.six.moves.urllib.parse import urljoin from ..request import RequestMethods from ..response import HTTPResponse -from ..util.timeout import Timeout from ..util.retry import Retry +from ..util.timeout import Timeout from . import _appengine_environ try: @@ -90,29 +90,30 @@ class AppEngineManager(RequestMethods): * If you attempt to use this on App Engine Flexible, as full socket support is available. * If a request size is more than 10 megabytes. - * If a response size is more than 32 megabtyes. + * If a response size is more than 32 megabytes. * If you use an unsupported request method such as OPTIONS. Beyond those cases, it will raise normal urllib3 errors. """ - def __init__(self, headers=None, retries=None, validate_certificate=True, - urlfetch_retries=True): + def __init__( + self, + headers=None, + retries=None, + validate_certificate=True, + urlfetch_retries=True, + ): if not urlfetch: raise AppEnginePlatformError( - "URLFetch is not available in this environment.") - - if is_prod_appengine_mvms(): - raise AppEnginePlatformError( - "Use normal urllib3.PoolManager instead of AppEngineManager" - "on Managed VMs, as using URLFetch is not necessary in " - "this environment.") + "URLFetch is not available in this environment." + ) warnings.warn( "urllib3 is using URLFetch on Google App Engine sandbox instead " "of sockets. To use sockets directly instead of URLFetch see " - "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", - AppEnginePlatformWarning) + "https://urllib3.readthedocs.io/en/1.26.x/reference/urllib3.contrib.html.", + AppEnginePlatformWarning, + ) RequestMethods.__init__(self, headers) self.validate_certificate = validate_certificate @@ -127,17 +128,22 @@ class AppEngineManager(RequestMethods): # Return False to re-raise any potential exceptions return False - def urlopen(self, method, url, body=None, headers=None, - retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, - **response_kw): + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=None, + redirect=True, + timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw + ): retries = self._get_retries(retries, redirect) try: - follow_redirects = ( - redirect and - retries.redirect != 0 and - retries.total) + follow_redirects = redirect and retries.redirect != 0 and retries.total response = urlfetch.fetch( url, payload=body, @@ -152,44 +158,52 @@ class AppEngineManager(RequestMethods): raise TimeoutError(self, e) except urlfetch.InvalidURLError as e: - if 'too large' in str(e): + if "too large" in str(e): raise AppEnginePlatformError( "URLFetch request too large, URLFetch only " - "supports requests up to 10mb in size.", e) + "supports requests up to 10mb in size.", + e, + ) raise ProtocolError(e) except urlfetch.DownloadError as e: - if 'Too many redirects' in str(e): + if "Too many redirects" in str(e): raise MaxRetryError(self, url, reason=e) raise ProtocolError(e) except urlfetch.ResponseTooLargeError as e: raise AppEnginePlatformError( "URLFetch response too large, URLFetch only supports" - "responses up to 32mb in size.", e) + "responses up to 32mb in size.", + e, + ) except urlfetch.SSLCertificateError as e: raise SSLError(e) except urlfetch.InvalidMethodError as e: raise AppEnginePlatformError( - "URLFetch does not support method: %s" % method, e) + "URLFetch does not support method: %s" % method, e + ) http_response = self._urlfetch_response_to_http_response( - response, retries=retries, **response_kw) + response, retries=retries, **response_kw + ) # Handle redirect? redirect_location = redirect and http_response.get_redirect_location() if redirect_location: # Check for redirect response - if (self.urlfetch_retries and retries.raise_on_redirect): + if self.urlfetch_retries and retries.raise_on_redirect: raise MaxRetryError(self, url, "too many redirects") else: if http_response.status == 303: - method = 'GET' + method = "GET" try: - retries = retries.increment(method, url, response=http_response, _pool=self) + retries = retries.increment( + method, url, response=http_response, _pool=self + ) except MaxRetryError: if retries.raise_on_redirect: raise MaxRetryError(self, url, "too many redirects") @@ -199,22 +213,32 @@ class AppEngineManager(RequestMethods): log.debug("Redirecting %s -> %s", url, redirect_location) redirect_url = urljoin(url, redirect_location) return self.urlopen( - method, redirect_url, body, headers, - retries=retries, redirect=redirect, - timeout=timeout, **response_kw) + method, + redirect_url, + body, + headers, + retries=retries, + redirect=redirect, + timeout=timeout, + **response_kw + ) # Check if we should retry the HTTP response. - has_retry_after = bool(http_response.getheader('Retry-After')) + has_retry_after = bool(http_response.getheader("Retry-After")) if retries.is_retry(method, http_response.status, has_retry_after): - retries = retries.increment( - method, url, response=http_response, _pool=self) + retries = retries.increment(method, url, response=http_response, _pool=self) log.debug("Retry: %s", url) retries.sleep(http_response) return self.urlopen( - method, url, - body=body, headers=headers, - retries=retries, redirect=redirect, - timeout=timeout, **response_kw) + method, + url, + body=body, + headers=headers, + retries=retries, + redirect=redirect, + timeout=timeout, + **response_kw + ) return http_response @@ -223,18 +247,18 @@ class AppEngineManager(RequestMethods): if is_prod_appengine(): # Production GAE handles deflate encoding automatically, but does # not remove the encoding header. - content_encoding = urlfetch_resp.headers.get('content-encoding') + content_encoding = urlfetch_resp.headers.get("content-encoding") - if content_encoding == 'deflate': - del urlfetch_resp.headers['content-encoding'] + if content_encoding == "deflate": + del urlfetch_resp.headers["content-encoding"] - transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') + transfer_encoding = urlfetch_resp.headers.get("transfer-encoding") # We have a full response's content, # so let's make sure we don't report ourselves as chunked data. - if transfer_encoding == 'chunked': + if transfer_encoding == "chunked": encodings = transfer_encoding.split(",") - encodings.remove('chunked') - urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) + encodings.remove("chunked") + urlfetch_resp.headers["transfer-encoding"] = ",".join(encodings) original_response = HTTPResponse( # In order for decoding to work, we must present the content as @@ -262,20 +286,21 @@ class AppEngineManager(RequestMethods): warnings.warn( "URLFetch does not support granular timeout settings, " "reverting to total or default URLFetch timeout.", - AppEnginePlatformWarning) + AppEnginePlatformWarning, + ) return timeout.total return timeout def _get_retries(self, retries, redirect): if not isinstance(retries, Retry): - retries = Retry.from_int( - retries, redirect=redirect, default=self.retries) + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) if retries.connect or retries.read or retries.redirect: warnings.warn( "URLFetch only supports total retries and does not " "recognize connect, read, or redirect retry parameters.", - AppEnginePlatformWarning) + AppEnginePlatformWarning, + ) return retries diff --git a/libs/common/urllib3/contrib/ntlmpool.py b/libs/common/urllib3/contrib/ntlmpool.py index 8ea127c5..41a8fd17 100644 --- a/libs/common/urllib3/contrib/ntlmpool.py +++ b/libs/common/urllib3/contrib/ntlmpool.py @@ -5,12 +5,21 @@ Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 """ from __future__ import absolute_import +import warnings from logging import getLogger + from ntlm import ntlm from .. import HTTPSConnectionPool from ..packages.six.moves.http_client import HTTPSConnection +warnings.warn( + "The 'urllib3.contrib.ntlmpool' module is deprecated and will be removed " + "in urllib3 v2.0 release, urllib3 is not able to support it properly due " + "to reasons listed in issue: https://github.com/urllib3/urllib3/issues/2282. " + "If you are a user of this module please comment in the mentioned issue.", + DeprecationWarning, +) log = getLogger(__name__) @@ -20,7 +29,7 @@ class NTLMConnectionPool(HTTPSConnectionPool): Implements an NTLM authentication version of an urllib3 connection pool """ - scheme = 'https' + scheme = "https" def __init__(self, user, pw, authurl, *args, **kwargs): """ @@ -31,7 +40,7 @@ class NTLMConnectionPool(HTTPSConnectionPool): super(NTLMConnectionPool, self).__init__(*args, **kwargs) self.authurl = authurl self.rawuser = user - user_parts = user.split('\\', 1) + user_parts = user.split("\\", 1) self.domain = user_parts[0].upper() self.user = user_parts[1] self.pw = pw @@ -40,72 +49,82 @@ class NTLMConnectionPool(HTTPSConnectionPool): # Performs the NTLM handshake that secures the connection. The socket # must be kept open while requests are performed. self.num_connections += 1 - log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', - self.num_connections, self.host, self.authurl) + log.debug( + "Starting NTLM HTTPS connection no. %d: https://%s%s", + self.num_connections, + self.host, + self.authurl, + ) - headers = {'Connection': 'Keep-Alive'} - req_header = 'Authorization' - resp_header = 'www-authenticate' + headers = {"Connection": "Keep-Alive"} + req_header = "Authorization" + resp_header = "www-authenticate" conn = HTTPSConnection(host=self.host, port=self.port) # Send negotiation message - headers[req_header] = ( - 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) - log.debug('Request headers: %s', headers) - conn.request('GET', self.authurl, None, headers) + headers[req_header] = "NTLM %s" % ntlm.create_NTLM_NEGOTIATE_MESSAGE( + self.rawuser + ) + log.debug("Request headers: %s", headers) + conn.request("GET", self.authurl, None, headers) res = conn.getresponse() reshdr = dict(res.getheaders()) - log.debug('Response status: %s %s', res.status, res.reason) - log.debug('Response headers: %s', reshdr) - log.debug('Response data: %s [...]', res.read(100)) + log.debug("Response status: %s %s", res.status, res.reason) + log.debug("Response headers: %s", reshdr) + log.debug("Response data: %s [...]", res.read(100)) # Remove the reference to the socket, so that it can not be closed by # the response object (we want to keep the socket open) res.fp = None # Server should respond with a challenge message - auth_header_values = reshdr[resp_header].split(', ') + auth_header_values = reshdr[resp_header].split(", ") auth_header_value = None for s in auth_header_values: - if s[:5] == 'NTLM ': + if s[:5] == "NTLM ": auth_header_value = s[5:] if auth_header_value is None: - raise Exception('Unexpected %s response header: %s' % - (resp_header, reshdr[resp_header])) + raise Exception( + "Unexpected %s response header: %s" % (resp_header, reshdr[resp_header]) + ) # Send authentication message - ServerChallenge, NegotiateFlags = \ - ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value) - auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, - self.user, - self.domain, - self.pw, - NegotiateFlags) - headers[req_header] = 'NTLM %s' % auth_msg - log.debug('Request headers: %s', headers) - conn.request('GET', self.authurl, None, headers) + ServerChallenge, NegotiateFlags = ntlm.parse_NTLM_CHALLENGE_MESSAGE( + auth_header_value + ) + auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE( + ServerChallenge, self.user, self.domain, self.pw, NegotiateFlags + ) + headers[req_header] = "NTLM %s" % auth_msg + log.debug("Request headers: %s", headers) + conn.request("GET", self.authurl, None, headers) res = conn.getresponse() - log.debug('Response status: %s %s', res.status, res.reason) - log.debug('Response headers: %s', dict(res.getheaders())) - log.debug('Response data: %s [...]', res.read()[:100]) + log.debug("Response status: %s %s", res.status, res.reason) + log.debug("Response headers: %s", dict(res.getheaders())) + log.debug("Response data: %s [...]", res.read()[:100]) if res.status != 200: if res.status == 401: - raise Exception('Server rejected request: wrong ' - 'username or password') - raise Exception('Wrong server response: %s %s' % - (res.status, res.reason)) + raise Exception("Server rejected request: wrong username or password") + raise Exception("Wrong server response: %s %s" % (res.status, res.reason)) res.fp = None - log.debug('Connection established') + log.debug("Connection established") return conn - def urlopen(self, method, url, body=None, headers=None, retries=3, - redirect=True, assert_same_host=True): + def urlopen( + self, + method, + url, + body=None, + headers=None, + retries=3, + redirect=True, + assert_same_host=True, + ): if headers is None: headers = {} - headers['Connection'] = 'Keep-Alive' - return super(NTLMConnectionPool, self).urlopen(method, url, body, - headers, retries, - redirect, - assert_same_host) + headers["Connection"] = "Keep-Alive" + return super(NTLMConnectionPool, self).urlopen( + method, url, body, headers, retries, redirect, assert_same_host + ) diff --git a/libs/common/urllib3/contrib/pyopenssl.py b/libs/common/urllib3/contrib/pyopenssl.py index 7c0e9465..1ed214b1 100644 --- a/libs/common/urllib3/contrib/pyopenssl.py +++ b/libs/common/urllib3/contrib/pyopenssl.py @@ -1,27 +1,31 @@ """ -SSL with SNI_-support for Python 2. Follow these instructions if you would -like to verify SSL certificates in Python 2. Note, the default libraries do +TLS with SNI_-support for Python 2. Follow these instructions if you would +like to verify TLS certificates in Python 2. Note, the default libraries do *not* do certificate checking; you need to do additional work to validate certificates yourself. This needs the following packages installed: -* pyOpenSSL (tested with 16.0.0) -* cryptography (minimum 1.3.4, from pyopenssl) -* idna (minimum 2.0, from cryptography) +* `pyOpenSSL`_ (tested with 16.0.0) +* `cryptography`_ (minimum 1.3.4, from pyopenssl) +* `idna`_ (minimum 2.0, from cryptography) However, pyopenssl depends on cryptography, which depends on idna, so while we use all three directly here we end up having relatively few packages required. You can install them with the following command: - pip install pyopenssl cryptography idna +.. code-block:: bash + + $ python -m pip install pyopenssl cryptography idna To activate certificate checking, call :func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code before you begin making HTTP requests. This can be done in a ``sitecustomize`` module, or at any other time before your application begins using ``urllib3``, -like this:: +like this: + +.. code-block:: python try: import urllib3.contrib.pyopenssl @@ -35,18 +39,19 @@ when the required modules are installed. Activating this module also has the positive side effect of disabling SSL/TLS compression in Python 2 (see `CRIME attack`_). -If you want to configure the default list of supported cipher suites, you can -set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. - .. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication .. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) +.. _pyopenssl: https://www.pyopenssl.org +.. _cryptography: https://cryptography.io +.. _idna: https://github.com/kjd/idna """ from __future__ import absolute_import +import OpenSSL.crypto import OpenSSL.SSL from cryptography import x509 from cryptography.hazmat.backends.openssl import backend as openssl_backend -from cryptography.hazmat.backends.openssl.x509 import _Certificate + try: from cryptography.x509 import UnsupportedExtension except ImportError: @@ -54,8 +59,10 @@ except ImportError: class UnsupportedExtension(Exception): pass -from socket import timeout, error as SocketError + from io import BytesIO +from socket import error as SocketError +from socket import timeout try: # Platform-specific: Python 2 from socket import _fileobject @@ -65,42 +72,50 @@ except ImportError: # Platform-specific: Python 3 import logging import ssl -from ..packages import six import sys +import warnings from .. import util +from ..packages import six +from ..util.ssl_ import PROTOCOL_TLS_CLIENT -__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] +warnings.warn( + "'urllib3.contrib.pyopenssl' module is deprecated and will be removed " + "in a future release of urllib3 2.x. Read more in this issue: " + "https://github.com/urllib3/urllib3/issues/2680", + category=DeprecationWarning, + stacklevel=2, +) + +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] # SNI always works. HAS_SNI = True # Map from urllib3 to PyOpenSSL compatible parameter-values. _openssl_versions = { - ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, + util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, + PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, } -if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): +if hasattr(ssl, "PROTOCOL_SSLv3") and hasattr(OpenSSL.SSL, "SSLv3_METHOD"): + _openssl_versions[ssl.PROTOCOL_SSLv3] = OpenSSL.SSL.SSLv3_METHOD + +if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD -if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): +if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD -try: - _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) -except AttributeError: - pass _stdlib_to_openssl_verify = { ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, - ssl.CERT_REQUIRED: - OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, + ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER + + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, } -_openssl_to_stdlib_verify = dict( - (v, k) for k, v in _stdlib_to_openssl_verify.items() -) +_openssl_to_stdlib_verify = dict((v, k) for k, v in _stdlib_to_openssl_verify.items()) # OpenSSL will only write 16K at a time SSL_WRITE_BLOCKSIZE = 16384 @@ -113,10 +128,11 @@ log = logging.getLogger(__name__) def inject_into_urllib3(): - 'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.' + "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." _validate_dependencies_met() + util.SSLContext = PyOpenSSLContext util.ssl_.SSLContext = PyOpenSSLContext util.HAS_SNI = HAS_SNI util.ssl_.HAS_SNI = HAS_SNI @@ -125,8 +141,9 @@ def inject_into_urllib3(): def extract_from_urllib3(): - 'Undo monkey-patching by :func:`inject_into_urllib3`.' + "Undo monkey-patching by :func:`inject_into_urllib3`." + util.SSLContext = orig_util_SSLContext util.ssl_.SSLContext = orig_util_SSLContext util.HAS_SNI = orig_util_HAS_SNI util.ssl_.HAS_SNI = orig_util_HAS_SNI @@ -141,17 +158,23 @@ def _validate_dependencies_met(): """ # Method added in `cryptography==1.1`; not available in older versions from cryptography.x509.extensions import Extensions + if getattr(Extensions, "get_extension_for_class", None) is None: - raise ImportError("'cryptography' module missing required functionality. " - "Try upgrading to v1.3.4 or newer.") + raise ImportError( + "'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer." + ) # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 # attribute is only present on those versions. from OpenSSL.crypto import X509 + x509 = X509() if getattr(x509, "_x509", None) is None: - raise ImportError("'pyOpenSSL' module missing required functionality. " - "Try upgrading to v0.14 or newer.") + raise ImportError( + "'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer." + ) def _dnsname_to_stdlib(name): @@ -167,6 +190,7 @@ def _dnsname_to_stdlib(name): If the name cannot be idna-encoded then we return None signalling that the name given should be skipped. """ + def idna_encode(name): """ Borrowed wholesale from the Python Cryptography Project. It turns out @@ -176,19 +200,23 @@ def _dnsname_to_stdlib(name): import idna try: - for prefix in [u'*.', u'.']: + for prefix in [u"*.", u"."]: if name.startswith(prefix): - name = name[len(prefix):] - return prefix.encode('ascii') + idna.encode(name) + name = name[len(prefix) :] + return prefix.encode("ascii") + idna.encode(name) return idna.encode(name) except idna.core.IDNAError: return None + # Don't send IPv6 addresses through the IDNA encoder. + if ":" in name: + return name + name = idna_encode(name) if name is None: return None elif sys.version_info >= (3, 0): - name = name.decode('utf-8') + name = name.decode("utf-8") return name @@ -200,21 +228,22 @@ def get_subj_alt_name(peer_cert): if hasattr(peer_cert, "to_cryptography"): cert = peer_cert.to_cryptography() else: - # This is technically using private APIs, but should work across all - # relevant versions before PyOpenSSL got a proper API for this. - cert = _Certificate(openssl_backend, peer_cert._x509) + der = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, peer_cert) + cert = x509.load_der_x509_certificate(der, openssl_backend) # We want to find the SAN extension. Ask Cryptography to locate it (it's # faster than looping in Python) try: - ext = cert.extensions.get_extension_for_class( - x509.SubjectAlternativeName - ).value + ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value except x509.ExtensionNotFound: # No such extension, return the empty list. return [] - except (x509.DuplicateExtension, UnsupportedExtension, - x509.UnsupportedGeneralNameType, UnicodeError) as e: + except ( + x509.DuplicateExtension, + UnsupportedExtension, + x509.UnsupportedGeneralNameType, + UnicodeError, + ) as e: # A problem has been found with the quality of the certificate. Assume # no SAN field is present. log.warning( @@ -233,23 +262,23 @@ def get_subj_alt_name(peer_cert): # does with certificates, and so we need to attempt to do the same. # We also want to skip over names which cannot be idna encoded. names = [ - ('DNS', name) for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) + ("DNS", name) + for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) if name is not None ] names.extend( - ('IP Address', str(name)) - for name in ext.get_values_for_type(x509.IPAddress) + ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) ) return names class WrappedSocket(object): - '''API-compatibility wrapper for Python OpenSSL's Connection-class. + """API-compatibility wrapper for Python OpenSSL's Connection-class. Note: _makefile_refs, _drop() and _reuse() are needed for the garbage collector of pypy. - ''' + """ def __init__(self, connection, socket, suppress_ragged_eofs=True): self.connection = connection @@ -272,20 +301,24 @@ class WrappedSocket(object): try: data = self.connection.recv(*args, **kwargs) except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): - return b'' + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return b"" else: raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError as e: + except OpenSSL.SSL.ZeroReturnError: if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return b'' + return b"" else: raise except OpenSSL.SSL.WantReadError: if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout('The read operation timed out') + raise timeout("The read operation timed out") else: return self.recv(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("read error: %r" % e) else: return data @@ -293,21 +326,25 @@ class WrappedSocket(object): try: return self.connection.recv_into(*args, **kwargs) except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): return 0 else: raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError as e: + except OpenSSL.SSL.ZeroReturnError: if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: return 0 else: raise except OpenSSL.SSL.WantReadError: if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout('The read operation timed out') + raise timeout("The read operation timed out") else: return self.recv_into(*args, **kwargs) + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("read error: %r" % e) + def settimeout(self, timeout): return self.socket.settimeout(timeout) @@ -325,7 +362,9 @@ class WrappedSocket(object): def sendall(self, data): total_sent = 0 while total_sent < len(data): - sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + sent = self._send_until_done( + data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] + ) total_sent += sent def shutdown(self): @@ -349,17 +388,16 @@ class WrappedSocket(object): return x509 if binary_form: - return OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_ASN1, - x509) + return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) return { - 'subject': ( - (('commonName', x509.get_subject().CN),), - ), - 'subjectAltName': get_subj_alt_name(x509) + "subject": ((("commonName", x509.get_subject().CN),),), + "subjectAltName": get_subj_alt_name(x509), } + def version(self): + return self.connection.get_protocol_version_name() + def _reuse(self): self._makefile_refs += 1 @@ -371,9 +409,11 @@ class WrappedSocket(object): if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): self._makefile_refs += 1 return _fileobject(self, mode, bufsize, close=True) + else: # Platform-specific: Python 3 makefile = backport_makefile @@ -386,6 +426,7 @@ class PyOpenSSLContext(object): for translating the interface of the standard library ``SSLContext`` object to calls into PyOpenSSL. """ + def __init__(self, protocol): self.protocol = _openssl_versions[protocol] self._ctx = OpenSSL.SSL.Context(self.protocol) @@ -407,41 +448,52 @@ class PyOpenSSLContext(object): @verify_mode.setter def verify_mode(self, value): - self._ctx.set_verify( - _stdlib_to_openssl_verify[value], - _verify_callback - ) + self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) def set_default_verify_paths(self): self._ctx.set_default_verify_paths() def set_ciphers(self, ciphers): if isinstance(ciphers, six.text_type): - ciphers = ciphers.encode('utf-8') + ciphers = ciphers.encode("utf-8") self._ctx.set_cipher_list(ciphers) def load_verify_locations(self, cafile=None, capath=None, cadata=None): if cafile is not None: - cafile = cafile.encode('utf-8') + cafile = cafile.encode("utf-8") if capath is not None: - capath = capath.encode('utf-8') - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) + capath = capath.encode("utf-8") + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError("unable to load trusted certificates: %r" % e) def load_cert_chain(self, certfile, keyfile=None, password=None): self._ctx.use_certificate_chain_file(certfile) if password is not None: - self._ctx.set_passwd_cb(lambda max_length, prompt_twice, userdata: password) + if not isinstance(password, six.binary_type): + password = password.encode("utf-8") + self._ctx.set_passwd_cb(lambda *_: password) self._ctx.use_privatekey_file(keyfile or certfile) - def wrap_socket(self, sock, server_side=False, - do_handshake_on_connect=True, suppress_ragged_eofs=True, - server_hostname=None): + def set_alpn_protocols(self, protocols): + protocols = [six.ensure_binary(p) for p in protocols] + return self._ctx.set_alpn_protos(protocols) + + def wrap_socket( + self, + sock, + server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + server_hostname=None, + ): cnx = OpenSSL.SSL.Connection(self._ctx, sock) if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 - server_hostname = server_hostname.encode('utf-8') + server_hostname = server_hostname.encode("utf-8") if server_hostname is not None: cnx.set_tlsext_host_name(server_hostname) @@ -453,10 +505,10 @@ class PyOpenSSLContext(object): cnx.do_handshake() except OpenSSL.SSL.WantReadError: if not util.wait_for_read(sock, sock.gettimeout()): - raise timeout('select timed out') + raise timeout("select timed out") continue except OpenSSL.SSL.Error as e: - raise ssl.SSLError('bad handshake: %r' % e) + raise ssl.SSLError("bad handshake: %r" % e) break return WrappedSocket(cnx, sock) diff --git a/libs/common/urllib3/contrib/securetransport.py b/libs/common/urllib3/contrib/securetransport.py index 77cb59ed..6c46a3b9 100644 --- a/libs/common/urllib3/contrib/securetransport.py +++ b/libs/common/urllib3/contrib/securetransport.py @@ -23,6 +23,33 @@ To use this module, simply import and inject it:: urllib3.contrib.securetransport.inject_into_urllib3() Happy TLSing! + +This code is a bastardised version of the code found in Will Bond's oscrypto +library. An enormous debt is owed to him for blazing this trail for us. For +that reason, this code should be considered to be covered both by urllib3's +license and by oscrypto's: + +.. code-block:: + + Copyright (c) 2015-2016 Will Bond + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. """ from __future__ import absolute_import @@ -33,16 +60,22 @@ import os.path import shutil import socket import ssl +import struct import threading import weakref +import six + from .. import util -from ._securetransport.bindings import ( - Security, SecurityConst, CoreFoundation -) +from ..util.ssl_ import PROTOCOL_TLS_CLIENT +from ._securetransport.bindings import CoreFoundation, Security, SecurityConst from ._securetransport.low_level import ( - _assert_no_error, _cert_array_from_pem, _temporary_keychain, - _load_client_cert_chain + _assert_no_error, + _build_tls_unknown_ca_alert, + _cert_array_from_pem, + _create_cfstring_array, + _load_client_cert_chain, + _temporary_keychain, ) try: # Platform-specific: Python 2 @@ -51,7 +84,7 @@ except ImportError: # Platform-specific: Python 3 _fileobject = None from ..packages.backports.makefile import backport_makefile -__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] # SNI always works HAS_SNI = True @@ -86,35 +119,32 @@ SSL_WRITE_BLOCKSIZE = 16384 # individual cipher suites. We need to do this because this is how # SecureTransport wants them. CIPHER_SUITES = [ - SecurityConst.TLS_AES_256_GCM_SHA384, - SecurityConst.TLS_CHACHA20_POLY1305_SHA256, - SecurityConst.TLS_AES_128_GCM_SHA256, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - SecurityConst.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, - SecurityConst.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, - SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_AES_256_GCM_SHA384, + SecurityConst.TLS_AES_128_GCM_SHA256, SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_AES_128_CCM_8_SHA256, + SecurityConst.TLS_AES_128_CCM_SHA256, SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, @@ -123,38 +153,44 @@ CIPHER_SUITES = [ # Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of # TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. +# TLSv1 to 1.2 are supported on macOS 10.8+ _protocol_to_min_max = { - ssl.PROTOCOL_SSLv23: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), + util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), + PROTOCOL_TLS_CLIENT: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), } if hasattr(ssl, "PROTOCOL_SSLv2"): _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( - SecurityConst.kSSLProtocol2, SecurityConst.kSSLProtocol2 + SecurityConst.kSSLProtocol2, + SecurityConst.kSSLProtocol2, ) if hasattr(ssl, "PROTOCOL_SSLv3"): _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( - SecurityConst.kSSLProtocol3, SecurityConst.kSSLProtocol3 + SecurityConst.kSSLProtocol3, + SecurityConst.kSSLProtocol3, ) if hasattr(ssl, "PROTOCOL_TLSv1"): _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( - SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol1 + SecurityConst.kTLSProtocol1, + SecurityConst.kTLSProtocol1, ) if hasattr(ssl, "PROTOCOL_TLSv1_1"): _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( - SecurityConst.kTLSProtocol11, SecurityConst.kTLSProtocol11 + SecurityConst.kTLSProtocol11, + SecurityConst.kTLSProtocol11, ) if hasattr(ssl, "PROTOCOL_TLSv1_2"): _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( - SecurityConst.kTLSProtocol12, SecurityConst.kTLSProtocol12 + SecurityConst.kTLSProtocol12, + SecurityConst.kTLSProtocol12, ) -if hasattr(ssl, "PROTOCOL_TLS"): - _protocol_to_min_max[ssl.PROTOCOL_TLS] = _protocol_to_min_max[ssl.PROTOCOL_SSLv23] def inject_into_urllib3(): """ Monkey-patch urllib3 with SecureTransport-backed SSL-support. """ + util.SSLContext = SecureTransportContext util.ssl_.SSLContext = SecureTransportContext util.HAS_SNI = HAS_SNI util.ssl_.HAS_SNI = HAS_SNI @@ -166,6 +202,7 @@ def extract_from_urllib3(): """ Undo monkey-patching by :func:`inject_into_urllib3`. """ + util.SSLContext = orig_util_SSLContext util.ssl_.SSLContext = orig_util_SSLContext util.HAS_SNI = orig_util_HAS_SNI util.ssl_.HAS_SNI = orig_util_HAS_SNI @@ -195,7 +232,7 @@ def _read_callback(connection_id, data_buffer, data_length_pointer): while read_count < requested_length: if timeout is None or timeout >= 0: if not util.wait_for_read(base_socket, timeout): - raise socket.error(errno.EAGAIN, 'timed out') + raise socket.error(errno.EAGAIN, "timed out") remaining = requested_length - read_count buffer = (ctypes.c_char * remaining).from_address( @@ -251,7 +288,7 @@ def _write_callback(connection_id, data_buffer, data_length_pointer): while sent < bytes_to_write: if timeout is None or timeout >= 0: if not util.wait_for_write(base_socket, timeout): - raise socket.error(errno.EAGAIN, 'timed out') + raise socket.error(errno.EAGAIN, "timed out") chunk_sent = base_socket.send(data) sent += chunk_sent @@ -293,6 +330,7 @@ class WrappedSocket(object): Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage collector of PyPy. """ + def __init__(self, socket): self.socket = socket self.context = None @@ -345,19 +383,58 @@ class WrappedSocket(object): ) _assert_no_error(result) + def _set_alpn_protocols(self, protocols): + """ + Sets up the ALPN protocols on the context. + """ + if not protocols: + return + protocols_arr = _create_cfstring_array(protocols) + try: + result = Security.SSLSetALPNProtocols(self.context, protocols_arr) + _assert_no_error(result) + finally: + CoreFoundation.CFRelease(protocols_arr) + def _custom_validate(self, verify, trust_bundle): """ Called when we have set custom validation. We do this in two cases: first, when cert validation is entirely disabled; and second, when using a custom trust DB. + Raises an SSLError if the connection is not trusted. """ # If we disabled cert validation, just say: cool. if not verify: return + successes = ( + SecurityConst.kSecTrustResultUnspecified, + SecurityConst.kSecTrustResultProceed, + ) + try: + trust_result = self._evaluate_trust(trust_bundle) + if trust_result in successes: + return + reason = "error code: %d" % (trust_result,) + except Exception as e: + # Do not trust on error + reason = "exception: %r" % (e,) + + # SecureTransport does not send an alert nor shuts down the connection. + rec = _build_tls_unknown_ca_alert(self.version()) + self.socket.sendall(rec) + # close the connection immediately + # l_onoff = 1, activate linger + # l_linger = 0, linger for 0 seoncds + opts = struct.pack("ii", 1, 0) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, opts) + self.close() + raise ssl.SSLError("certificate verify failed, %s" % reason) + + def _evaluate_trust(self, trust_bundle): # We want data in memory, so load it up. if os.path.isfile(trust_bundle): - with open(trust_bundle, 'rb') as f: + with open(trust_bundle, "rb") as f: trust_bundle = f.read() cert_array = None @@ -371,9 +448,7 @@ class WrappedSocket(object): # created for this connection, shove our CAs into it, tell ST to # ignore everything else it knows, and then ask if it can build a # chain. This is a buuuunch of code. - result = Security.SSLCopyPeerTrust( - self.context, ctypes.byref(trust) - ) + result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) _assert_no_error(result) if not trust: raise ssl.SSLError("Failed to copy trust reference") @@ -385,9 +460,7 @@ class WrappedSocket(object): _assert_no_error(result) trust_result = Security.SecTrustResultType() - result = Security.SecTrustEvaluate( - trust, ctypes.byref(trust_result) - ) + result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result)) _assert_no_error(result) finally: if trust: @@ -396,26 +469,20 @@ class WrappedSocket(object): if cert_array is not None: CoreFoundation.CFRelease(cert_array) - # Ok, now we can look at what the result was. - successes = ( - SecurityConst.kSecTrustResultUnspecified, - SecurityConst.kSecTrustResultProceed - ) - if trust_result.value not in successes: - raise ssl.SSLError( - "certificate verify failed, error code: %d" % - trust_result.value - ) + return trust_result.value - def handshake(self, - server_hostname, - verify, - trust_bundle, - min_version, - max_version, - client_cert, - client_key, - client_key_passphrase): + def handshake( + self, + server_hostname, + verify, + trust_bundle, + min_version, + max_version, + client_cert, + client_key, + client_key_passphrase, + alpn_protocols, + ): """ Actually performs the TLS handshake. This is run automatically by wrapped socket, and shouldn't be needed in user code. @@ -445,7 +512,7 @@ class WrappedSocket(object): # If we have a server hostname, we should set that too. if server_hostname: if not isinstance(server_hostname, bytes): - server_hostname = server_hostname.encode('utf-8') + server_hostname = server_hostname.encode("utf-8") result = Security.SSLSetPeerDomainName( self.context, server_hostname, len(server_hostname) @@ -455,9 +522,13 @@ class WrappedSocket(object): # Setup the ciphers. self._set_ciphers() + # Setup the ALPN protocols. + self._set_alpn_protocols(alpn_protocols) + # Set the minimum and maximum TLS versions. result = Security.SSLSetProtocolVersionMin(self.context, min_version) _assert_no_error(result) + result = Security.SSLSetProtocolVersionMax(self.context, max_version) _assert_no_error(result) @@ -467,9 +538,7 @@ class WrappedSocket(object): # authing in that case. if not verify or trust_bundle is not None: result = Security.SSLSetSessionOption( - self.context, - SecurityConst.kSSLSessionOptionBreakOnServerAuth, - True + self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True ) _assert_no_error(result) @@ -479,9 +548,7 @@ class WrappedSocket(object): self._client_cert_chain = _load_client_cert_chain( self._keychain, client_cert, client_key ) - result = Security.SSLSetCertificate( - self.context, self._client_cert_chain - ) + result = Security.SSLSetCertificate(self.context, self._client_cert_chain) _assert_no_error(result) while True: @@ -532,7 +599,7 @@ class WrappedSocket(object): # There are some result codes that we want to treat as "not always # errors". Specifically, those are errSSLWouldBlock, # errSSLClosedGraceful, and errSSLClosedNoNotify. - if (result == SecurityConst.errSSLWouldBlock): + if result == SecurityConst.errSSLWouldBlock: # If we didn't process any bytes, then this was just a time out. # However, we can get errSSLWouldBlock in situations when we *did* # read some data, and in those cases we should just read "short" @@ -540,7 +607,10 @@ class WrappedSocket(object): if processed_bytes.value == 0: # Timed out, no data read. raise socket.timeout("recv timed out") - elif result in (SecurityConst.errSSLClosedGraceful, SecurityConst.errSSLClosedNoNotify): + elif result in ( + SecurityConst.errSSLClosedGraceful, + SecurityConst.errSSLClosedNoNotify, + ): # The remote peer has closed this connection. We should do so as # well. Note that we don't actually return here because in # principle this could actually be fired along with return data. @@ -579,7 +649,7 @@ class WrappedSocket(object): def sendall(self, data): total_sent = 0 while total_sent < len(data): - sent = self.send(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]) total_sent += sent def shutdown(self): @@ -626,18 +696,14 @@ class WrappedSocket(object): # instead to just flag to urllib3 that it shouldn't do its own hostname # validation when using SecureTransport. if not binary_form: - raise ValueError( - "SecureTransport only supports dumping binary certs" - ) + raise ValueError("SecureTransport only supports dumping binary certs") trust = Security.SecTrustRef() certdata = None der_bytes = None try: # Grab the trust store. - result = Security.SSLCopyPeerTrust( - self.context, ctypes.byref(trust) - ) + result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) _assert_no_error(result) if not trust: # Probably we haven't done the handshake yet. No biggie. @@ -667,6 +733,27 @@ class WrappedSocket(object): return der_bytes + def version(self): + protocol = Security.SSLProtocol() + result = Security.SSLGetNegotiatedProtocolVersion( + self.context, ctypes.byref(protocol) + ) + _assert_no_error(result) + if protocol.value == SecurityConst.kTLSProtocol13: + raise ssl.SSLError("SecureTransport does not support TLS 1.3") + elif protocol.value == SecurityConst.kTLSProtocol12: + return "TLSv1.2" + elif protocol.value == SecurityConst.kTLSProtocol11: + return "TLSv1.1" + elif protocol.value == SecurityConst.kTLSProtocol1: + return "TLSv1" + elif protocol.value == SecurityConst.kSSLProtocol3: + return "SSLv3" + elif protocol.value == SecurityConst.kSSLProtocol2: + return "SSLv2" + else: + raise ssl.SSLError("Unknown TLS version: %r" % protocol) + def _reuse(self): self._makefile_refs += 1 @@ -678,16 +765,20 @@ class WrappedSocket(object): if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): self._makefile_refs += 1 return _fileobject(self, mode, bufsize, close=True) + else: # Platform-specific: Python 3 + def makefile(self, mode="r", buffering=None, *args, **kwargs): # We disable buffering with SecureTransport because it conflicts with # the buffering that ST does internally (see issue #1153 for more). buffering = 0 return backport_makefile(self, mode, buffering, *args, **kwargs) + WrappedSocket.makefile = makefile @@ -697,6 +788,7 @@ class SecureTransportContext(object): interface of the standard library ``SSLContext`` object to calls into SecureTransport. """ + def __init__(self, protocol): self._min_version, self._max_version = _protocol_to_min_max[protocol] self._options = 0 @@ -705,6 +797,7 @@ class SecureTransportContext(object): self._client_cert = None self._client_key = None self._client_key_passphrase = None + self._alpn_protocols = None @property def check_hostname(self): @@ -763,16 +856,17 @@ class SecureTransportContext(object): def set_ciphers(self, ciphers): # For now, we just require the default cipher string. if ciphers != util.ssl_.DEFAULT_CIPHERS: - raise ValueError( - "SecureTransport doesn't support custom cipher strings" - ) + raise ValueError("SecureTransport doesn't support custom cipher strings") def load_verify_locations(self, cafile=None, capath=None, cadata=None): # OK, we only really support cadata and cafile. if capath is not None: - raise ValueError( - "SecureTransport does not support cert directories" - ) + raise ValueError("SecureTransport does not support cert directories") + + # Raise if cafile does not exist. + if cafile is not None: + with open(cafile): + pass self._trust_bundle = cafile or cadata @@ -781,9 +875,26 @@ class SecureTransportContext(object): self._client_key = keyfile self._client_cert_passphrase = password - def wrap_socket(self, sock, server_side=False, - do_handshake_on_connect=True, suppress_ragged_eofs=True, - server_hostname=None): + def set_alpn_protocols(self, protocols): + """ + Sets the ALPN protocols that will later be set on the context. + + Raises a NotImplementedError if ALPN is not supported. + """ + if not hasattr(Security, "SSLSetALPNProtocols"): + raise NotImplementedError( + "SecureTransport supports ALPN only in macOS 10.12+" + ) + self._alpn_protocols = [six.ensure_binary(p) for p in protocols] + + def wrap_socket( + self, + sock, + server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + server_hostname=None, + ): # So, what do we do here? Firstly, we assert some properties. This is a # stripped down shim, so there is some functionality we don't support. # See PEP 543 for the real deal. @@ -797,8 +908,14 @@ class SecureTransportContext(object): # Now we can handshake wrapped_socket.handshake( - server_hostname, self._verify, self._trust_bundle, - self._min_version, self._max_version, self._client_cert, - self._client_key, self._client_key_passphrase + server_hostname, + self._verify, + self._trust_bundle, + self._min_version, + self._max_version, + self._client_cert, + self._client_key, + self._client_key_passphrase, + self._alpn_protocols, ) return wrapped_socket diff --git a/libs/common/urllib3/contrib/socks.py b/libs/common/urllib3/contrib/socks.py index 811e312e..c326e80d 100644 --- a/libs/common/urllib3/contrib/socks.py +++ b/libs/common/urllib3/contrib/socks.py @@ -1,25 +1,42 @@ # -*- coding: utf-8 -*- """ This module contains provisional support for SOCKS proxies from within -urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and +urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and SOCKS5. To enable its functionality, either install PySocks or install this module with the ``socks`` extra. The SOCKS implementation supports the full range of urllib3 features. It also supports the following SOCKS features: -- SOCKS4 -- SOCKS4a -- SOCKS5 +- SOCKS4A (``proxy_url='socks4a://...``) +- SOCKS4 (``proxy_url='socks4://...``) +- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) +- SOCKS5 with local DNS (``proxy_url='socks5://...``) - Usernames and passwords for the SOCKS proxy -Known Limitations: +.. note:: + It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in + your ``proxy_url`` to ensure that DNS resolution is done from the remote + server instead of client-side when connecting to a domain name. + +SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 +supports IPv4, IPv6, and domain names. + +When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` +will be sent as the ``userid`` section of the SOCKS request: + +.. code-block:: python + + proxy_url="socks4a://@proxy-host" + +When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion +of the ``proxy_url`` will be sent as the username/password to authenticate +with the proxy: + +.. code-block:: python + + proxy_url="socks5h://:@proxy-host" -- Currently PySocks does not support contacting remote websites via literal - IPv6 addresses. Any such connection attempt will fail. You must use a domain - name. -- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any - such connection attempt will fail. """ from __future__ import absolute_import @@ -27,25 +44,24 @@ try: import socks except ImportError: import warnings + from ..exceptions import DependencyWarning - warnings.warn(( - 'SOCKS support in urllib3 requires the installation of optional ' - 'dependencies: specifically, PySocks. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies' + warnings.warn( + ( + "SOCKS support in urllib3 requires the installation of optional " + "dependencies: specifically, PySocks. For more information, see " + "https://urllib3.readthedocs.io/en/1.26.x/contrib.html#socks-proxies" ), - DependencyWarning + DependencyWarning, ) raise -from socket import error as SocketError, timeout as SocketTimeout +from socket import error as SocketError +from socket import timeout as SocketTimeout -from ..connection import ( - HTTPConnection, HTTPSConnection -) -from ..connectionpool import ( - HTTPConnectionPool, HTTPSConnectionPool -) +from ..connection import HTTPConnection, HTTPSConnection +from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool from ..exceptions import ConnectTimeoutError, NewConnectionError from ..poolmanager import PoolManager from ..util.url import parse_url @@ -60,8 +76,9 @@ class SOCKSConnection(HTTPConnection): """ A plain-text HTTP connection that connects via a SOCKS proxy. """ + def __init__(self, *args, **kwargs): - self._socks_options = kwargs.pop('_socks_options') + self._socks_options = kwargs.pop("_socks_options") super(SOCKSConnection, self).__init__(*args, **kwargs) def _new_conn(self): @@ -70,28 +87,30 @@ class SOCKSConnection(HTTPConnection): """ extra_kw = {} if self.source_address: - extra_kw['source_address'] = self.source_address + extra_kw["source_address"] = self.source_address if self.socket_options: - extra_kw['socket_options'] = self.socket_options + extra_kw["socket_options"] = self.socket_options try: conn = socks.create_connection( (self.host, self.port), - proxy_type=self._socks_options['socks_version'], - proxy_addr=self._socks_options['proxy_host'], - proxy_port=self._socks_options['proxy_port'], - proxy_username=self._socks_options['username'], - proxy_password=self._socks_options['password'], - proxy_rdns=self._socks_options['rdns'], + proxy_type=self._socks_options["socks_version"], + proxy_addr=self._socks_options["proxy_host"], + proxy_port=self._socks_options["proxy_port"], + proxy_username=self._socks_options["username"], + proxy_password=self._socks_options["password"], + proxy_rdns=self._socks_options["rdns"], timeout=self.timeout, **extra_kw ) - except SocketTimeout as e: + except SocketTimeout: raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout)) + self, + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), + ) except socks.ProxyError as e: # This is fragile as hell, but it seems to be the only way to raise @@ -101,23 +120,22 @@ class SOCKSConnection(HTTPConnection): if isinstance(error, SocketTimeout): raise ConnectTimeoutError( self, - "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout) + "Connection to %s timed out. (connect timeout=%s)" + % (self.host, self.timeout), ) else: raise NewConnectionError( - self, - "Failed to establish a new connection: %s" % error + self, "Failed to establish a new connection: %s" % error ) else: raise NewConnectionError( - self, - "Failed to establish a new connection: %s" % e + self, "Failed to establish a new connection: %s" % e ) except SocketError as e: # Defensive: PySocks should catch all these. raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e) + self, "Failed to establish a new connection: %s" % e + ) return conn @@ -143,47 +161,53 @@ class SOCKSProxyManager(PoolManager): A version of the urllib3 ProxyManager that routes connections via the defined SOCKS proxy. """ + pool_classes_by_scheme = { - 'http': SOCKSHTTPConnectionPool, - 'https': SOCKSHTTPSConnectionPool, + "http": SOCKSHTTPConnectionPool, + "https": SOCKSHTTPSConnectionPool, } - def __init__(self, proxy_url, username=None, password=None, - num_pools=10, headers=None, **connection_pool_kw): + def __init__( + self, + proxy_url, + username=None, + password=None, + num_pools=10, + headers=None, + **connection_pool_kw + ): parsed = parse_url(proxy_url) if username is None and password is None and parsed.auth is not None: - split = parsed.auth.split(':') + split = parsed.auth.split(":") if len(split) == 2: username, password = split - if parsed.scheme == 'socks5': + if parsed.scheme == "socks5": socks_version = socks.PROXY_TYPE_SOCKS5 rdns = False - elif parsed.scheme == 'socks5h': + elif parsed.scheme == "socks5h": socks_version = socks.PROXY_TYPE_SOCKS5 rdns = True - elif parsed.scheme == 'socks4': + elif parsed.scheme == "socks4": socks_version = socks.PROXY_TYPE_SOCKS4 rdns = False - elif parsed.scheme == 'socks4a': + elif parsed.scheme == "socks4a": socks_version = socks.PROXY_TYPE_SOCKS4 rdns = True else: - raise ValueError( - "Unable to determine SOCKS version from %s" % proxy_url - ) + raise ValueError("Unable to determine SOCKS version from %s" % proxy_url) self.proxy_url = proxy_url socks_options = { - 'socks_version': socks_version, - 'proxy_host': parsed.host, - 'proxy_port': parsed.port, - 'username': username, - 'password': password, - 'rdns': rdns + "socks_version": socks_version, + "proxy_host": parsed.host, + "proxy_port": parsed.port, + "username": username, + "password": password, + "rdns": rdns, } - connection_pool_kw['_socks_options'] = socks_options + connection_pool_kw["_socks_options"] = socks_options super(SOCKSProxyManager, self).__init__( num_pools, headers, **connection_pool_kw diff --git a/libs/common/urllib3/exceptions.py b/libs/common/urllib3/exceptions.py index 7bbaa987..cba6f3f5 100644 --- a/libs/common/urllib3/exceptions.py +++ b/libs/common/urllib3/exceptions.py @@ -1,22 +1,25 @@ from __future__ import absolute_import -from .packages.six.moves.http_client import ( - IncompleteRead as httplib_IncompleteRead -) + +from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead + # Base Exceptions class HTTPError(Exception): - "Base exception used by this module." + """Base exception used by this module.""" + pass class HTTPWarning(Warning): - "Base warning used by this module." + """Base warning used by this module.""" + pass class PoolError(HTTPError): - "Base exception for errors caused within a pool." + """Base exception for errors caused within a pool.""" + def __init__(self, pool, message): self.pool = pool HTTPError.__init__(self, "%s: %s" % (pool, message)) @@ -27,7 +30,8 @@ class PoolError(HTTPError): class RequestError(PoolError): - "Base exception for PoolErrors that have associated URLs." + """Base exception for PoolErrors that have associated URLs.""" + def __init__(self, pool, url, message): self.url = url PoolError.__init__(self, pool, message) @@ -38,22 +42,28 @@ class RequestError(PoolError): class SSLError(HTTPError): - "Raised when SSL certificate fails in an HTTPS connection." + """Raised when SSL certificate fails in an HTTPS connection.""" + pass class ProxyError(HTTPError): - "Raised when the connection to a proxy fails." - pass + """Raised when the connection to a proxy fails.""" + + def __init__(self, message, error, *args): + super(ProxyError, self).__init__(message, error, *args) + self.original_error = error class DecodeError(HTTPError): - "Raised when automatic decoding based on Content-Type fails." + """Raised when automatic decoding based on Content-Type fails.""" + pass class ProtocolError(HTTPError): - "Raised when something unexpected happens mid-request/response." + """Raised when something unexpected happens mid-request/response.""" + pass @@ -63,6 +73,7 @@ ConnectionError = ProtocolError # Leaf Exceptions + class MaxRetryError(RequestError): """Raised when the maximum number of retries is exceeded. @@ -76,14 +87,13 @@ class MaxRetryError(RequestError): def __init__(self, pool, url, reason=None): self.reason = reason - message = "Max retries exceeded with url: %s (Caused by %r)" % ( - url, reason) + message = "Max retries exceeded with url: %s (Caused by %r)" % (url, reason) RequestError.__init__(self, pool, url, message) class HostChangedError(RequestError): - "Raised when an existing pool gets a request for a foreign host." + """Raised when an existing pool gets a request for a foreign host.""" def __init__(self, pool, url, retries=3): message = "Tried to open a foreign host with url: %s" % url @@ -92,53 +102,61 @@ class HostChangedError(RequestError): class TimeoutStateError(HTTPError): - """ Raised when passing an invalid state to a timeout """ + """Raised when passing an invalid state to a timeout""" + pass class TimeoutError(HTTPError): - """ Raised when a socket timeout error occurs. + """Raised when a socket timeout error occurs. Catching this error will catch both :exc:`ReadTimeoutErrors ` and :exc:`ConnectTimeoutErrors `. """ + pass class ReadTimeoutError(TimeoutError, RequestError): - "Raised when a socket timeout occurs while receiving data from a server" + """Raised when a socket timeout occurs while receiving data from a server""" + pass # This timeout error does not have a URL attached and needs to inherit from the # base HTTPError class ConnectTimeoutError(TimeoutError): - "Raised when a socket timeout occurs while connecting to a server" + """Raised when a socket timeout occurs while connecting to a server""" + pass class NewConnectionError(ConnectTimeoutError, PoolError): - "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" + pass class EmptyPoolError(PoolError): - "Raised when a pool runs out of connections and no more are allowed." + """Raised when a pool runs out of connections and no more are allowed.""" + pass class ClosedPoolError(PoolError): - "Raised when a request enters a pool after the pool has been closed." + """Raised when a request enters a pool after the pool has been closed.""" + pass class LocationValueError(ValueError, HTTPError): - "Raised when there is something wrong with a given URL input." + """Raised when there is something wrong with a given URL input.""" + pass class LocationParseError(LocationValueError): - "Raised when get_host or similar fails to parse the URL input." + """Raised when get_host or similar fails to parse the URL input.""" def __init__(self, location): message = "Failed to parse: %s" % location @@ -147,39 +165,56 @@ class LocationParseError(LocationValueError): self.location = location +class URLSchemeUnknown(LocationValueError): + """Raised when a URL input has an unsupported scheme.""" + + def __init__(self, scheme): + message = "Not supported URL scheme %s" % scheme + super(URLSchemeUnknown, self).__init__(message) + + self.scheme = scheme + + class ResponseError(HTTPError): - "Used as a container for an error reason supplied in a MaxRetryError." - GENERIC_ERROR = 'too many error responses' - SPECIFIC_ERROR = 'too many {status_code} error responses' + """Used as a container for an error reason supplied in a MaxRetryError.""" + + GENERIC_ERROR = "too many error responses" + SPECIFIC_ERROR = "too many {status_code} error responses" class SecurityWarning(HTTPWarning): - "Warned when performing security reducing actions" + """Warned when performing security reducing actions""" + pass class SubjectAltNameWarning(SecurityWarning): - "Warned when connecting to a host with a certificate missing a SAN." + """Warned when connecting to a host with a certificate missing a SAN.""" + pass class InsecureRequestWarning(SecurityWarning): - "Warned when making an unverified HTTPS request." + """Warned when making an unverified HTTPS request.""" + pass class SystemTimeWarning(SecurityWarning): - "Warned when system time is suspected to be wrong" + """Warned when system time is suspected to be wrong""" + pass class InsecurePlatformWarning(SecurityWarning): - "Warned when certain SSL configuration is not available on a platform." + """Warned when certain TLS/SSL configuration is not available on a platform.""" + pass class SNIMissingWarning(HTTPWarning): - "Warned when making a HTTPS request without SNI available." + """Warned when making a HTTPS request without SNI available.""" + pass @@ -188,19 +223,22 @@ class DependencyWarning(HTTPWarning): Warned when an attempt is made to import a module with missing optional dependencies. """ + pass class ResponseNotChunked(ProtocolError, ValueError): - "Response needs to be chunked in order to read it as chunks." + """Response needs to be chunked in order to read it as chunks.""" + pass class BodyNotHttplibCompatible(HTTPError): """ - Body should be httplib.HTTPResponse like (have an fp attribute which - returns raw chunks) for read_chunked(). + Body should be :class:`http.client.HTTPResponse` like + (have an fp attribute which returns raw chunks) for read_chunked(). """ + pass @@ -208,39 +246,78 @@ class IncompleteRead(HTTPError, httplib_IncompleteRead): """ Response length doesn't match expected Content-Length - Subclass of http_client.IncompleteRead to allow int value - for `partial` to avoid creating large objects on streamed - reads. + Subclass of :class:`http.client.IncompleteRead` to allow int value + for ``partial`` to avoid creating large objects on streamed reads. """ + def __init__(self, partial, expected): super(IncompleteRead, self).__init__(partial, expected) def __repr__(self): - return ('IncompleteRead(%i bytes read, ' - '%i more expected)' % (self.partial, self.expected)) + return "IncompleteRead(%i bytes read, %i more expected)" % ( + self.partial, + self.expected, + ) + + +class InvalidChunkLength(HTTPError, httplib_IncompleteRead): + """Invalid chunk length in a chunked response.""" + + def __init__(self, response, length): + super(InvalidChunkLength, self).__init__( + response.tell(), response.length_remaining + ) + self.response = response + self.length = length + + def __repr__(self): + return "InvalidChunkLength(got length %r, %i bytes read)" % ( + self.length, + self.partial, + ) class InvalidHeader(HTTPError): - "The header provided was somehow invalid." + """The header provided was somehow invalid.""" + pass -class ProxySchemeUnknown(AssertionError, ValueError): - "ProxyManager does not support the supplied scheme" +class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): + """ProxyManager does not support the supplied scheme""" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. def __init__(self, scheme): - message = "Not supported proxy scheme %s" % scheme + # 'localhost' is here because our URL parser parses + # localhost:8080 -> scheme=localhost, remove if we fix this. + if scheme == "localhost": + scheme = None + if scheme is None: + message = "Proxy URL had no scheme, should start with http:// or https://" + else: + message = ( + "Proxy URL had unsupported scheme %s, should use http:// or https://" + % scheme + ) super(ProxySchemeUnknown, self).__init__(message) +class ProxySchemeUnsupported(ValueError): + """Fetching HTTPS resources through HTTPS proxies is unsupported""" + + pass + + class HeaderParsingError(HTTPError): - "Raised by assert_header_parsing, but we convert it to a log.warning statement." + """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" + def __init__(self, defects, unparsed_data): - message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) + message = "%s, unparsed data: %r" % (defects or "Unknown", unparsed_data) super(HeaderParsingError, self).__init__(message) class UnrewindableBodyError(HTTPError): - "urllib3 encountered an error when trying to rewind a body" + """urllib3 encountered an error when trying to rewind a body""" + pass diff --git a/libs/common/urllib3/fields.py b/libs/common/urllib3/fields.py index 37fe64a3..9d630f49 100644 --- a/libs/common/urllib3/fields.py +++ b/libs/common/urllib3/fields.py @@ -1,11 +1,13 @@ from __future__ import absolute_import + import email.utils import mimetypes +import re from .packages import six -def guess_content_type(filename, default='application/octet-stream'): +def guess_content_type(filename, default="application/octet-stream"): """ Guess the "Content-Type" of a file. @@ -19,57 +21,143 @@ def guess_content_type(filename, default='application/octet-stream'): return default -def format_header_param(name, value): +def format_header_param_rfc2231(name, value): """ - Helper function to format and quote a single header parameter. + Helper function to format and quote a single header parameter using the + strategy defined in RFC 2231. Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows RFC 2231, as - suggested by RFC 2388 Section 4.4. + non-ASCII values, like file names. This follows + `RFC 2388 Section 4.4 `_. :param name: The name of the parameter, a string expected to be ASCII only. :param value: - The value of the parameter, provided as a unicode string. + The value of the parameter, provided as ``bytes`` or `str``. + :ret: + An RFC-2231-formatted unicode string. """ + if isinstance(value, six.binary_type): + value = value.decode("utf-8") + if not any(ch in value for ch in '"\\\r\n'): - result = '%s="%s"' % (name, value) + result = u'%s="%s"' % (name, value) try: - result.encode('ascii') + result.encode("ascii") except (UnicodeEncodeError, UnicodeDecodeError): pass else: return result - if not six.PY3 and isinstance(value, six.text_type): # Python 2: - value = value.encode('utf-8') - value = email.utils.encode_rfc2231(value, 'utf-8') - value = '%s*=%s' % (name, value) + + if six.PY2: # Python 2: + value = value.encode("utf-8") + + # encode_rfc2231 accepts an encoded string and returns an ascii-encoded + # string in Python 2 but accepts and returns unicode strings in Python 3 + value = email.utils.encode_rfc2231(value, "utf-8") + value = "%s*=%s" % (name, value) + + if six.PY2: # Python 2: + value = value.decode("utf-8") + return value +_HTML5_REPLACEMENTS = { + u"\u0022": u"%22", + # Replace "\" with "\\". + u"\u005C": u"\u005C\u005C", +} + +# All control characters from 0x00 to 0x1F *except* 0x1B. +_HTML5_REPLACEMENTS.update( + { + six.unichr(cc): u"%{:02X}".format(cc) + for cc in range(0x00, 0x1F + 1) + if cc not in (0x1B,) + } +) + + +def _replace_multiple(value, needles_and_replacements): + def replacer(match): + return needles_and_replacements[match.group(0)] + + pattern = re.compile( + r"|".join([re.escape(needle) for needle in needles_and_replacements.keys()]) + ) + + result = pattern.sub(replacer, value) + + return result + + +def format_header_param_html5(name, value): + """ + Helper function to format and quote a single header parameter using the + HTML5 strategy. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows the `HTML5 Working Draft + Section 4.10.22.7`_ and matches the behavior of curl and modern browsers. + + .. _HTML5 Working Draft Section 4.10.22.7: + https://w3c.github.io/html/sec-forms.html#multipart-form-data + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as ``bytes`` or `str``. + :ret: + A unicode string, stripped of troublesome characters. + """ + if isinstance(value, six.binary_type): + value = value.decode("utf-8") + + value = _replace_multiple(value, _HTML5_REPLACEMENTS) + + return u'%s="%s"' % (name, value) + + +# For backwards-compatibility. +format_header_param = format_header_param_html5 + + class RequestField(object): """ A data container for request body parameters. :param name: - The name of this request field. + The name of this request field. Must be unicode. :param data: The data/value body. :param filename: - An optional filename of the request field. + An optional filename of the request field. Must be unicode. :param headers: An optional dict-like object of headers to initially use for the field. + :param header_formatter: + An optional callable that is used to encode and format the headers. By + default, this is :func:`format_header_param_html5`. """ - def __init__(self, name, data, filename=None, headers=None): + + def __init__( + self, + name, + data, + filename=None, + headers=None, + header_formatter=format_header_param_html5, + ): self._name = name self._filename = filename self.data = data self.headers = {} if headers: self.headers = dict(headers) + self.header_formatter = header_formatter @classmethod - def from_tuples(cls, fieldname, value): + def from_tuples(cls, fieldname, value, header_formatter=format_header_param_html5): """ A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. @@ -97,21 +185,25 @@ class RequestField(object): content_type = None data = value - request_param = cls(fieldname, data, filename=filename) + request_param = cls( + fieldname, data, filename=filename, header_formatter=header_formatter + ) request_param.make_multipart(content_type=content_type) return request_param def _render_part(self, name, value): """ - Overridable helper function to format a single header parameter. + Overridable helper function to format a single header parameter. By + default, this calls ``self.header_formatter``. :param name: The name of the parameter, a string expected to be ASCII only. :param value: The value of the parameter, provided as a unicode string. """ - return format_header_param(name, value) + + return self.header_formatter(name, value) def _render_parts(self, header_parts): """ @@ -133,7 +225,7 @@ class RequestField(object): if value is not None: parts.append(self._render_part(name, value)) - return '; '.join(parts) + return u"; ".join(parts) def render_headers(self): """ @@ -141,21 +233,22 @@ class RequestField(object): """ lines = [] - sort_keys = ['Content-Disposition', 'Content-Type', 'Content-Location'] + sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] for sort_key in sort_keys: if self.headers.get(sort_key, False): - lines.append('%s: %s' % (sort_key, self.headers[sort_key])) + lines.append(u"%s: %s" % (sort_key, self.headers[sort_key])) for header_name, header_value in self.headers.items(): if header_name not in sort_keys: if header_value: - lines.append('%s: %s' % (header_name, header_value)) + lines.append(u"%s: %s" % (header_name, header_value)) - lines.append('\r\n') - return '\r\n'.join(lines) + lines.append(u"\r\n") + return u"\r\n".join(lines) - def make_multipart(self, content_disposition=None, content_type=None, - content_location=None): + def make_multipart( + self, content_disposition=None, content_type=None, content_location=None + ): """ Makes this request field into a multipart request field. @@ -168,11 +261,14 @@ class RequestField(object): The 'Content-Location' of the request body. """ - self.headers['Content-Disposition'] = content_disposition or 'form-data' - self.headers['Content-Disposition'] += '; '.join([ - '', self._render_parts( - (('name', self._name), ('filename', self._filename)) - ) - ]) - self.headers['Content-Type'] = content_type - self.headers['Content-Location'] = content_location + self.headers["Content-Disposition"] = content_disposition or u"form-data" + self.headers["Content-Disposition"] += u"; ".join( + [ + u"", + self._render_parts( + ((u"name", self._name), (u"filename", self._filename)) + ), + ] + ) + self.headers["Content-Type"] = content_type + self.headers["Content-Location"] = content_location diff --git a/libs/common/urllib3/filepost.py b/libs/common/urllib3/filepost.py index 78f1e19b..36c9252c 100644 --- a/libs/common/urllib3/filepost.py +++ b/libs/common/urllib3/filepost.py @@ -1,15 +1,15 @@ from __future__ import absolute_import + import binascii import codecs import os - from io import BytesIO +from .fields import RequestField from .packages import six from .packages.six import b -from .fields import RequestField -writer = codecs.lookup('utf-8')[3] +writer = codecs.lookup("utf-8")[3] def choose_boundary(): @@ -17,8 +17,8 @@ def choose_boundary(): Our embarrassingly-simple replacement for mimetools.choose_boundary. """ boundary = binascii.hexlify(os.urandom(16)) - if six.PY3: - boundary = boundary.decode('ascii') + if not six.PY2: + boundary = boundary.decode("ascii") return boundary @@ -76,7 +76,7 @@ def encode_multipart_formdata(fields, boundary=None): boundary = choose_boundary() for field in iter_field_objects(fields): - body.write(b('--%s\r\n' % (boundary))) + body.write(b("--%s\r\n" % (boundary))) writer(body).write(field.render_headers()) data = field.data @@ -89,10 +89,10 @@ def encode_multipart_formdata(fields, boundary=None): else: body.write(data) - body.write(b'\r\n') + body.write(b"\r\n") - body.write(b('--%s--\r\n' % (boundary))) + body.write(b("--%s--\r\n" % (boundary))) - content_type = str('multipart/form-data; boundary=%s' % boundary) + content_type = str("multipart/form-data; boundary=%s" % boundary) return body.getvalue(), content_type diff --git a/libs/common/urllib3/packages/__init__.py b/libs/common/urllib3/packages/__init__.py index 170e974c..e69de29b 100644 --- a/libs/common/urllib3/packages/__init__.py +++ b/libs/common/urllib3/packages/__init__.py @@ -1,5 +0,0 @@ -from __future__ import absolute_import - -from . import ssl_match_hostname - -__all__ = ('ssl_match_hostname', ) diff --git a/libs/common/urllib3/packages/backports/makefile.py b/libs/common/urllib3/packages/backports/makefile.py index 740db377..b8fb2154 100644 --- a/libs/common/urllib3/packages/backports/makefile.py +++ b/libs/common/urllib3/packages/backports/makefile.py @@ -7,19 +7,17 @@ Backports the Python 3 ``socket.makefile`` method for use with anything that wants to create a "fake" socket object. """ import io - from socket import SocketIO -def backport_makefile(self, mode="r", buffering=None, encoding=None, - errors=None, newline=None): +def backport_makefile( + self, mode="r", buffering=None, encoding=None, errors=None, newline=None +): """ Backport of ``socket.makefile`` from Python 3.5. """ if not set(mode) <= {"r", "w", "b"}: - raise ValueError( - "invalid mode %r (only r, w, b allowed)" % (mode,) - ) + raise ValueError("invalid mode %r (only r, w, b allowed)" % (mode,)) writing = "w" in mode reading = "r" in mode or not writing assert reading or writing diff --git a/libs/common/urllib3/packages/six.py b/libs/common/urllib3/packages/six.py index 190c0239..f099a3dc 100644 --- a/libs/common/urllib3/packages/six.py +++ b/libs/common/urllib3/packages/six.py @@ -1,6 +1,4 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson +# Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,6 +18,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +"""Utilities for writing code that runs on Python 2 and 3""" + from __future__ import absolute_import import functools @@ -29,7 +29,7 @@ import sys import types __author__ = "Benjamin Peterson " -__version__ = "1.10.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -38,15 +38,15 @@ PY3 = sys.version_info[0] == 3 PY34 = sys.version_info[0:2] >= (3, 4) if PY3: - string_types = str, - integer_types = int, - class_types = type, + string_types = (str,) + integer_types = (int,) + class_types = (type,) text_type = str binary_type = bytes MAXSIZE = sys.maxsize else: - string_types = basestring, + string_types = (basestring,) integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode @@ -58,9 +58,9 @@ else: else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): - def __len__(self): return 1 << 31 + try: len(X()) except OverflowError: @@ -71,6 +71,11 @@ else: MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -84,7 +89,6 @@ def _import_module(name): class _LazyDescr(object): - def __init__(self, name): self.name = name @@ -101,7 +105,6 @@ class _LazyDescr(object): class MovedModule(_LazyDescr): - def __init__(self, name, old, new=None): super(MovedModule, self).__init__(name) if PY3: @@ -122,7 +125,6 @@ class MovedModule(_LazyDescr): class _LazyModule(types.ModuleType): - def __init__(self, name): super(_LazyModule, self).__init__(name) self.__doc__ = self.__class__.__doc__ @@ -137,7 +139,6 @@ class _LazyModule(types.ModuleType): class MovedAttribute(_LazyDescr): - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): super(MovedAttribute, self).__init__(name) if PY3: @@ -186,6 +187,11 @@ class _SixMetaPathImporter(object): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -221,28 +227,42 @@ class _SixMetaPathImporter(object): Required, if is_package is implemented""" self.__get_module(fullname) # eventually raises ImportError return None + get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + + _importer = _SixMetaPathImporter(__name__) class _MovedItems(_LazyModule): """Lazy loading of moved objects""" + __path__ = [] # mark as package _moved_attributes = [ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute( + "filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse" + ), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute( + "reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload" + ), MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), @@ -251,21 +271,36 @@ _moved_attributes = [ MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedAttribute( + "zip_longest", "itertools", "itertools", "izip_longest", "zip_longest" + ), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), + MovedModule( + "collections_abc", + "collections", + "collections.abc" if sys.version_info >= (3, 3) else "collections", + ), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule( + "_dummy_thread", + "dummy_thread", + "_dummy_thread" if sys.version_info < (3, 9) else "_thread", + ), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule( + "email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart" + ), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), @@ -283,15 +318,12 @@ _moved_attributes = [ MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), + MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), MovedModule("tkinter_font", "tkFont", "tkinter.font"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), @@ -337,10 +369,14 @@ _urllib_parse_moved_attributes = [ MovedAttribute("quote_plus", "urllib", "urllib.parse"), MovedAttribute("unquote", "urllib", "urllib.parse"), MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute( + "unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes" + ), MovedAttribute("urlencode", "urllib", "urllib.parse"), MovedAttribute("splitquery", "urllib", "urllib.parse"), MovedAttribute("splittag", "urllib", "urllib.parse"), MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), MovedAttribute("uses_params", "urlparse", "urllib.parse"), @@ -353,8 +389,11 @@ del attr Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") +_importer._add_module( + Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", + "moves.urllib.parse", +) class Module_six_moves_urllib_error(_LazyModule): @@ -373,8 +412,11 @@ del attr Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") +_importer._add_module( + Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", + "moves.urllib.error", +) class Module_six_moves_urllib_request(_LazyModule): @@ -416,6 +458,8 @@ _urllib_request_moved_attributes = [ MovedAttribute("URLopener", "urllib", "urllib.request"), MovedAttribute("FancyURLopener", "urllib", "urllib.request"), MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), ] for attr in _urllib_request_moved_attributes: setattr(Module_six_moves_urllib_request, attr.name, attr) @@ -423,8 +467,11 @@ del attr Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") +_importer._add_module( + Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", + "moves.urllib.request", +) class Module_six_moves_urllib_response(_LazyModule): @@ -444,8 +491,11 @@ del attr Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") +_importer._add_module( + Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", + "moves.urllib.response", +) class Module_six_moves_urllib_robotparser(_LazyModule): @@ -460,15 +510,21 @@ for attr in _urllib_robotparser_moved_attributes: setattr(Module_six_moves_urllib_robotparser, attr.name, attr) del attr -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes +Module_six_moves_urllib_robotparser._moved_attributes = ( + _urllib_robotparser_moved_attributes +) -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") +_importer._add_module( + Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", + "moves.urllib.robotparser", +) class Module_six_moves_urllib(types.ModuleType): """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package parse = _importer._get_module("moves.urllib_parse") error = _importer._get_module("moves.urllib_error") @@ -477,10 +533,12 @@ class Module_six_moves_urllib(types.ModuleType): robotparser = _importer._get_module("moves.urllib_robotparser") def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] + return ["parse", "error", "request", "response", "robotparser"] -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") + +_importer._add_module( + Module_six_moves_urllib(__name__ + ".moves.urllib"), "moves.urllib" +) def add_move(move): @@ -520,19 +578,24 @@ else: try: advance_iterator = next except NameError: + def advance_iterator(it): return it.next() + + next = advance_iterator try: callable = callable except NameError: + def callable(obj): return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) if PY3: + def get_unbound_function(unbound): return unbound @@ -543,6 +606,7 @@ if PY3: Iterator = object else: + def get_unbound_function(unbound): return unbound.im_func @@ -553,13 +617,13 @@ else: return types.MethodType(func, None, cls) class Iterator(object): - def next(self): return type(self).__next__(self) callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") +_add_doc( + get_unbound_function, """Get the function out of a possibly unbound function""" +) get_method_function = operator.attrgetter(_meth_func) @@ -571,6 +635,7 @@ get_function_globals = operator.attrgetter(_func_globals) if PY3: + def iterkeys(d, **kw): return iter(d.keys(**kw)) @@ -589,6 +654,7 @@ if PY3: viewitems = operator.methodcaller("items") else: + def iterkeys(d, **kw): return d.iterkeys(**kw) @@ -609,42 +675,52 @@ else: _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") _add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") +_add_doc(iteritems, "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc( + iterlists, "Return an iterator over the (key, [values]) pairs of a dictionary." +) if PY3: + def b(s): return s.encode("latin-1") def u(s): return s + unichr = chr import struct + int2byte = struct.Struct(">B").pack del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io + StringIO = io.StringIO BytesIO = io.BytesIO + del io _assertCountEqual = "assertCountEqual" if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" else: + def b(s): return s + # Workaround for standalone backslash def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + return unicode(s.replace(r"\\", r"\\\\"), "unicode_escape") + unichr = unichr int2byte = chr @@ -653,12 +729,15 @@ else: def indexbytes(buf, i): return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) import StringIO + StringIO = BytesIO = StringIO.StringIO _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") @@ -675,17 +754,26 @@ def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None else: + def exec_(_code_, _globs_=None, _locs_=None): """Execute code in a namespace.""" if _globs_ is None: @@ -696,30 +784,36 @@ else: del frame elif _locs_ is None: _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") + exec ("""exec _code_ in _globs_, _locs_""") - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") + exec_( + """def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""" + ) -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") +if sys.version_info[:2] > (3,): + exec_( + """def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""" + ) else: + def raise_from(value, from_value): raise value print_ = getattr(moves.builtins, "print", None) if print_ is None: + def print_(*args, **kwargs): """The new-style print function for Python 2.4 and 2.5.""" fp = kwargs.pop("file", sys.stdout) @@ -730,14 +824,17 @@ if print_ is None: if not isinstance(data, basestring): data = str(data) # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): + if ( + isinstance(fp, file) + and isinstance(data, unicode) + and fp.encoding is not None + ): errors = getattr(fp, "errors", None) if errors is None: errors = "strict" data = data.encode(fp.encoding, errors) fp.write(data) + want_unicode = False sep = kwargs.pop("sep", None) if sep is not None: @@ -773,6 +870,8 @@ if print_ is None: write(sep) write(arg) write(end) + + if sys.version_info[:2] < (3, 3): _print = print_ @@ -783,16 +882,46 @@ if sys.version_info[:2] < (3, 3): if flush and fp is not None: fp.flush() + _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper( + wrapper, + wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES, + ): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped return wrapper + + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps( + wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES, + ): + return functools.partial( + _update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated + ) + + wraps.__doc__ = functools.wraps.__doc__ + else: wraps = functools.wraps @@ -802,44 +931,121 @@ def with_metaclass(meta, *bases): # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. - class metaclass(meta): - + class metaclass(type): def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d["__orig_bases__"] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + + return type.__new__(metaclass, "temporary_class", (), {}) def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') + slots = orig_vars.get("__slots__") if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) + orig_vars.pop("__dict__", None) + orig_vars.pop("__weakref__", None) + if hasattr(cls, "__qualname__"): + orig_vars["__qualname__"] = cls.__qualname__ return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper +def ensure_binary(s, encoding="utf-8", errors="strict"): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding="utf-8", errors="strict"): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding="utf-8", errors="strict"): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + def python_2_unicode_compatible(klass): """ - A decorator that defines __unicode__ and __str__ methods under Python 2. + A class decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method returning text and apply this decorator to the class. """ if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) + if "__str__" not in klass.__dict__: + raise ValueError( + "@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % klass.__name__ + ) klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + klass.__str__ = lambda self: self.__unicode__().encode("utf-8") return klass @@ -859,8 +1065,10 @@ if sys.meta_path: # be floating around. Therefore, we can't use isinstance() to check for # the six meta path importer, since the other six instance will have # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): + if ( + type(importer).__name__ == "_SixMetaPathImporter" + and importer.name == __name__ + ): del sys.meta_path[i] break del i, importer diff --git a/libs/common/urllib3/packages/ssl_match_hostname/__init__.py b/libs/common/urllib3/packages/ssl_match_hostname/__init__.py deleted file mode 100644 index d6594eb2..00000000 --- a/libs/common/urllib3/packages/ssl_match_hostname/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -import sys - -try: - # Our match_hostname function is the same as 3.5's, so we only want to - # import the match_hostname function if it's at least that good. - if sys.version_info < (3, 5): - raise ImportError("Fallback to vendored code") - - from ssl import CertificateError, match_hostname -except ImportError: - try: - # Backport of the function from a pypi module - from backports.ssl_match_hostname import CertificateError, match_hostname - except ImportError: - # Our vendored copy - from ._implementation import CertificateError, match_hostname - -# Not needed, but documenting what we provide. -__all__ = ('CertificateError', 'match_hostname') diff --git a/libs/common/urllib3/poolmanager.py b/libs/common/urllib3/poolmanager.py index fe5491cf..ca4ec341 100644 --- a/libs/common/urllib3/poolmanager.py +++ b/libs/common/urllib3/poolmanager.py @@ -1,58 +1,79 @@ from __future__ import absolute_import + import collections import functools import logging from ._collections import RecentlyUsedContainer -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from .connectionpool import port_by_scheme -from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + ProxySchemeUnsupported, + URLSchemeUnknown, +) +from .packages import six from .packages.six.moves.urllib.parse import urljoin from .request import RequestMethods -from .util.url import parse_url +from .util.proxy import connection_requires_http_tunnel from .util.retry import Retry +from .util.url import parse_url - -__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] +__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] log = logging.getLogger(__name__) -SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', - 'ssl_version', 'ca_cert_dir', 'ssl_context') +SSL_KEYWORDS = ( + "key_file", + "cert_file", + "cert_reqs", + "ca_certs", + "ssl_version", + "ca_cert_dir", + "ssl_context", + "key_password", + "server_hostname", +) # All known keyword arguments that could be provided to the pool manager, its # pools, or the underlying connections. This is used to construct a pool key. _key_fields = ( - 'key_scheme', # str - 'key_host', # str - 'key_port', # int - 'key_timeout', # int or float or Timeout - 'key_retries', # int or Retry - 'key_strict', # bool - 'key_block', # bool - 'key_source_address', # str - 'key_key_file', # str - 'key_cert_file', # str - 'key_cert_reqs', # str - 'key_ca_certs', # str - 'key_ssl_version', # str - 'key_ca_cert_dir', # str - 'key_ssl_context', # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext - 'key_maxsize', # int - 'key_headers', # dict - 'key__proxy', # parsed proxy url - 'key__proxy_headers', # dict - 'key_socket_options', # list of (level (int), optname (int), value (int or str)) tuples - 'key__socks_options', # dict - 'key_assert_hostname', # bool or string - 'key_assert_fingerprint', # str - 'key_server_hostname', #str + "key_scheme", # str + "key_host", # str + "key_port", # int + "key_timeout", # int or float or Timeout + "key_retries", # int or Retry + "key_strict", # bool + "key_block", # bool + "key_source_address", # str + "key_key_file", # str + "key_key_password", # str + "key_cert_file", # str + "key_cert_reqs", # str + "key_ca_certs", # str + "key_ssl_version", # str + "key_ca_cert_dir", # str + "key_ssl_context", # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext + "key_maxsize", # int + "key_headers", # dict + "key__proxy", # parsed proxy url + "key__proxy_headers", # dict + "key__proxy_config", # class + "key_socket_options", # list of (level (int), optname (int), value (int or str)) tuples + "key__socks_options", # dict + "key_assert_hostname", # bool or string + "key_assert_fingerprint", # str + "key_server_hostname", # str ) #: The namedtuple class used to construct keys for the connection pool. #: All custom key schemes should include the fields in this key at a minimum. -PoolKey = collections.namedtuple('PoolKey', _key_fields) +PoolKey = collections.namedtuple("PoolKey", _key_fields) + +_proxy_config_fields = ("ssl_context", "use_forwarding_for_https") +ProxyConfig = collections.namedtuple("ProxyConfig", _proxy_config_fields) def _default_key_normalizer(key_class, request_context): @@ -77,24 +98,24 @@ def _default_key_normalizer(key_class, request_context): """ # Since we mutate the dictionary, make a copy first context = request_context.copy() - context['scheme'] = context['scheme'].lower() - context['host'] = context['host'].lower() + context["scheme"] = context["scheme"].lower() + context["host"] = context["host"].lower() # These are both dictionaries and need to be transformed into frozensets - for key in ('headers', '_proxy_headers', '_socks_options'): + for key in ("headers", "_proxy_headers", "_socks_options"): if key in context and context[key] is not None: context[key] = frozenset(context[key].items()) # The socket_options key may be a list and needs to be transformed into a # tuple. - socket_opts = context.get('socket_options') + socket_opts = context.get("socket_options") if socket_opts is not None: - context['socket_options'] = tuple(socket_opts) + context["socket_options"] = tuple(socket_opts) # Map the kwargs to the names in the namedtuple - this is necessary since # namedtuples can't have fields starting with '_'. for key in list(context.keys()): - context['key_' + key] = context.pop(key) + context["key_" + key] = context.pop(key) # Default to ``None`` for keys missing from the context for field in key_class._fields: @@ -109,14 +130,11 @@ def _default_key_normalizer(key_class, request_context): #: Each PoolManager makes a copy of this dictionary so they can be configured #: globally here, or individually on the instance. key_fn_by_scheme = { - 'http': functools.partial(_default_key_normalizer, PoolKey), - 'https': functools.partial(_default_key_normalizer, PoolKey), + "http": functools.partial(_default_key_normalizer, PoolKey), + "https": functools.partial(_default_key_normalizer, PoolKey), } -pool_classes_by_scheme = { - 'http': HTTPConnectionPool, - 'https': HTTPSConnectionPool, -} +pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} class PoolManager(RequestMethods): @@ -148,12 +166,12 @@ class PoolManager(RequestMethods): """ proxy = None + proxy_config = None def __init__(self, num_pools=10, headers=None, **connection_pool_kw): RequestMethods.__init__(self, headers) self.connection_pool_kw = connection_pool_kw - self.pools = RecentlyUsedContainer(num_pools, - dispose_func=lambda p: p.close()) + self.pools = RecentlyUsedContainer(num_pools, dispose_func=lambda p: p.close()) # Locally set the pool classes and keys so other PoolManagers can # override them. @@ -170,7 +188,7 @@ class PoolManager(RequestMethods): def _new_pool(self, scheme, host, port, request_context=None): """ - Create a new :class:`ConnectionPool` based on host, port, scheme, and + Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and any additional pool keyword arguments. If ``request_context`` is provided, it is provided as keyword arguments @@ -186,10 +204,10 @@ class PoolManager(RequestMethods): # this function has historically only used the scheme, host, and port # in the positional args. When an API change is acceptable these can # be removed. - for key in ('scheme', 'host', 'port'): + for key in ("scheme", "host", "port"): request_context.pop(key, None) - if scheme == 'http': + if scheme == "http": for kw in SSL_KEYWORDS: request_context.pop(kw, None) @@ -204,9 +222,9 @@ class PoolManager(RequestMethods): """ self.pools.clear() - def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): """ - Get a :class:`ConnectionPool` based on the host, port, and scheme. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. If ``port`` isn't given, it will be derived from the ``scheme`` using ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is @@ -219,30 +237,32 @@ class PoolManager(RequestMethods): raise LocationValueError("No host specified.") request_context = self._merge_pool_kwargs(pool_kwargs) - request_context['scheme'] = scheme or 'http' + request_context["scheme"] = scheme or "http" if not port: - port = port_by_scheme.get(request_context['scheme'].lower(), 80) - request_context['port'] = port - request_context['host'] = host + port = port_by_scheme.get(request_context["scheme"].lower(), 80) + request_context["port"] = port + request_context["host"] = host return self.connection_from_context(request_context) def connection_from_context(self, request_context): """ - Get a :class:`ConnectionPool` based on the request context. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. ``request_context`` must at least contain the ``scheme`` key and its value must be a key in ``key_fn_by_scheme`` instance variable. """ - scheme = request_context['scheme'].lower() - pool_key_constructor = self.key_fn_by_scheme[scheme] + scheme = request_context["scheme"].lower() + pool_key_constructor = self.key_fn_by_scheme.get(scheme) + if not pool_key_constructor: + raise URLSchemeUnknown(scheme) pool_key = pool_key_constructor(request_context) return self.connection_from_pool_key(pool_key, request_context=request_context) def connection_from_pool_key(self, pool_key, request_context=None): """ - Get a :class:`ConnectionPool` based on the provided pool key. + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. ``pool_key`` should be a namedtuple that only contains immutable objects. At a minimum it must have the ``scheme``, ``host``, and @@ -256,9 +276,9 @@ class PoolManager(RequestMethods): return pool # Make a fresh ConnectionPool of the desired type - scheme = request_context['scheme'] - host = request_context['host'] - port = request_context['port'] + scheme = request_context["scheme"] + host = request_context["host"] + port = request_context["port"] pool = self._new_pool(scheme, host, port, request_context=request_context) self.pools[pool_key] = pool @@ -276,8 +296,9 @@ class PoolManager(RequestMethods): not used. """ u = parse_url(url) - return self.connection_from_host(u.host, port=u.port, scheme=u.scheme, - pool_kwargs=pool_kwargs) + return self.connection_from_host( + u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs + ) def _merge_pool_kwargs(self, override): """ @@ -299,9 +320,39 @@ class PoolManager(RequestMethods): base_pool_kwargs[key] = value return base_pool_kwargs + def _proxy_requires_url_absolute_form(self, parsed_url): + """ + Indicates if the proxy requires the complete destination URL in the + request. Normally this is only needed when not using an HTTP CONNECT + tunnel. + """ + if self.proxy is None: + return False + + return not connection_requires_http_tunnel( + self.proxy, self.proxy_config, parsed_url.scheme + ) + + def _validate_proxy_scheme_url_selection(self, url_scheme): + """ + Validates that were not attempting to do TLS in TLS connections on + Python2 or with unsupported SSL implementations. + """ + if self.proxy is None or url_scheme != "https": + return + + if self.proxy.scheme != "https": + return + + if six.PY2 and not self.proxy_config.use_forwarding_for_https: + raise ProxySchemeUnsupported( + "Contacting HTTPS destinations through HTTPS proxies " + "'via CONNECT tunnels' is not supported in Python 2" + ) + def urlopen(self, method, url, redirect=True, **kw): """ - Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + Same as :meth:`urllib3.HTTPConnectionPool.urlopen` with custom cross-host redirect logic and only sends the request-uri portion of the ``url``. @@ -309,15 +360,17 @@ class PoolManager(RequestMethods): :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. """ u = parse_url(url) + self._validate_proxy_scheme_url_selection(u.scheme) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) - kw['assert_same_host'] = False - kw['redirect'] = False + kw["assert_same_host"] = False + kw["redirect"] = False - if 'headers' not in kw: - kw['headers'] = self.headers.copy() + if "headers" not in kw: + kw["headers"] = self.headers.copy() - if self.proxy is not None and u.scheme == "http": + if self._proxy_requires_url_absolute_form(u): response = conn.urlopen(method, url, **kw) else: response = conn.urlopen(method, u.request_uri, **kw) @@ -331,31 +384,37 @@ class PoolManager(RequestMethods): # RFC 7231, Section 6.4.4 if response.status == 303: - method = 'GET' + method = "GET" - retries = kw.get('retries') + retries = kw.get("retries") if not isinstance(retries, Retry): retries = Retry.from_int(retries, redirect=redirect) # Strip headers marked as unsafe to forward to the redirected location. # Check remove_headers_on_redirect to avoid a potential network call within # conn.is_same_host() which may use socket.gethostbyname() in the future. - if (retries.remove_headers_on_redirect - and not conn.is_same_host(redirect_location)): - for header in retries.remove_headers_on_redirect: - kw['headers'].pop(header, None) + if retries.remove_headers_on_redirect and not conn.is_same_host( + redirect_location + ): + headers = list(six.iterkeys(kw["headers"])) + for header in headers: + if header.lower() in retries.remove_headers_on_redirect: + kw["headers"].pop(header, None) try: retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError: if retries.raise_on_redirect: + response.drain_conn() raise return response - kw['retries'] = retries - kw['redirect'] = redirect + kw["retries"] = retries + kw["redirect"] = redirect log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() return self.urlopen(method, redirect_location, **kw) @@ -373,6 +432,19 @@ class ProxyManager(PoolManager): HTTPS/CONNECT case they are sent only once. Could be used for proxy authentication. + :param proxy_ssl_context: + The proxy SSL context is used to establish the TLS connection to the + proxy when using HTTPS proxies. + + :param use_forwarding_for_https: + (Defaults to False) If set to True will forward requests to the HTTPS + proxy to be made on behalf of the client instead of creating a TLS + tunnel via the CONNECT method. **Enabling this flag means that request + and response headers and content will be visible from the HTTPS proxy** + whereas tunneling keeps request and response headers and content + private. IP address, target hostname, SNI, and port are always visible + to an HTTPS proxy even when this flag is disabled. + Example: >>> proxy = urllib3.ProxyManager('http://localhost:3128/') >>> r1 = proxy.request('GET', 'http://google.com/') @@ -386,47 +458,63 @@ class ProxyManager(PoolManager): """ - def __init__(self, proxy_url, num_pools=10, headers=None, - proxy_headers=None, **connection_pool_kw): + def __init__( + self, + proxy_url, + num_pools=10, + headers=None, + proxy_headers=None, + proxy_ssl_context=None, + use_forwarding_for_https=False, + **connection_pool_kw + ): if isinstance(proxy_url, HTTPConnectionPool): - proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host, - proxy_url.port) + proxy_url = "%s://%s:%i" % ( + proxy_url.scheme, + proxy_url.host, + proxy_url.port, + ) proxy = parse_url(proxy_url) - if not proxy.port: - port = port_by_scheme.get(proxy.scheme, 80) - proxy = proxy._replace(port=port) if proxy.scheme not in ("http", "https"): raise ProxySchemeUnknown(proxy.scheme) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + self.proxy = proxy self.proxy_headers = proxy_headers or {} + self.proxy_ssl_context = proxy_ssl_context + self.proxy_config = ProxyConfig(proxy_ssl_context, use_forwarding_for_https) - connection_pool_kw['_proxy'] = self.proxy - connection_pool_kw['_proxy_headers'] = self.proxy_headers + connection_pool_kw["_proxy"] = self.proxy + connection_pool_kw["_proxy_headers"] = self.proxy_headers + connection_pool_kw["_proxy_config"] = self.proxy_config - super(ProxyManager, self).__init__( - num_pools, headers, **connection_pool_kw) + super(ProxyManager, self).__init__(num_pools, headers, **connection_pool_kw) - def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): if scheme == "https": return super(ProxyManager, self).connection_from_host( - host, port, scheme, pool_kwargs=pool_kwargs) + host, port, scheme, pool_kwargs=pool_kwargs + ) return super(ProxyManager, self).connection_from_host( - self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs) + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs + ) def _set_proxy_headers(self, url, headers=None): """ Sets headers needed by proxies: specifically, the Accept and Host headers. Only sets headers not provided by the user. """ - headers_ = {'Accept': '*/*'} + headers_ = {"Accept": "*/*"} netloc = parse_url(url).netloc if netloc: - headers_['Host'] = netloc + headers_["Host"] = netloc if headers: headers_.update(headers) @@ -435,13 +523,12 @@ class ProxyManager(PoolManager): def urlopen(self, method, url, redirect=True, **kw): "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." u = parse_url(url) - - if u.scheme == "http": - # For proxied HTTPS requests, httplib sets the necessary headers - # on the CONNECT to the proxy. For HTTP, we'll definitely - # need to set 'Host' at the very least. - headers = kw.get('headers', self.headers) - kw['headers'] = self._set_proxy_headers(url, headers) + if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): + # For connections using HTTP CONNECT, httplib sets the necessary + # headers on the CONNECT to the proxy. If we're not using CONNECT, + # we'll definitely need to set 'Host' at the very least. + headers = kw.get("headers", self.headers) + kw["headers"] = self._set_proxy_headers(url, headers) return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) diff --git a/libs/common/urllib3/request.py b/libs/common/urllib3/request.py index 8f2f44bb..398386a5 100644 --- a/libs/common/urllib3/request.py +++ b/libs/common/urllib3/request.py @@ -3,15 +3,14 @@ from __future__ import absolute_import from .filepost import encode_multipart_formdata from .packages.six.moves.urllib.parse import urlencode - -__all__ = ['RequestMethods'] +__all__ = ["RequestMethods"] class RequestMethods(object): """ Convenience mixin for classes who implement a :meth:`urlopen` method, such - as :class:`~urllib3.connectionpool.HTTPConnectionPool` and - :class:`~urllib3.poolmanager.PoolManager`. + as :class:`urllib3.HTTPConnectionPool` and + :class:`urllib3.PoolManager`. Provides behavior for making common types of HTTP request methods and decides which type of request field encoding to use. @@ -36,16 +35,25 @@ class RequestMethods(object): explicitly. """ - _encode_url_methods = {'DELETE', 'GET', 'HEAD', 'OPTIONS'} + _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} def __init__(self, headers=None): self.headers = headers or {} - def urlopen(self, method, url, body=None, headers=None, - encode_multipart=True, multipart_boundary=None, - **kw): # Abstract - raise NotImplementedError("Classes extending RequestMethods must implement " - "their own ``urlopen`` method.") + def urlopen( + self, + method, + url, + body=None, + headers=None, + encode_multipart=True, + multipart_boundary=None, + **kw + ): # Abstract + raise NotImplementedError( + "Classes extending RequestMethods must implement " + "their own ``urlopen`` method." + ) def request(self, method, url, fields=None, headers=None, **urlopen_kw): """ @@ -60,19 +68,18 @@ class RequestMethods(object): """ method = method.upper() - urlopen_kw['request_url'] = url + urlopen_kw["request_url"] = url if method in self._encode_url_methods: - return self.request_encode_url(method, url, fields=fields, - headers=headers, - **urlopen_kw) + return self.request_encode_url( + method, url, fields=fields, headers=headers, **urlopen_kw + ) else: - return self.request_encode_body(method, url, fields=fields, - headers=headers, - **urlopen_kw) + return self.request_encode_body( + method, url, fields=fields, headers=headers, **urlopen_kw + ) - def request_encode_url(self, method, url, fields=None, headers=None, - **urlopen_kw): + def request_encode_url(self, method, url, fields=None, headers=None, **urlopen_kw): """ Make a request using :meth:`urlopen` with the ``fields`` encoded in the url. This is useful for request methods like GET, HEAD, DELETE, etc. @@ -80,25 +87,32 @@ class RequestMethods(object): if headers is None: headers = self.headers - extra_kw = {'headers': headers} + extra_kw = {"headers": headers} extra_kw.update(urlopen_kw) if fields: - url += '?' + urlencode(fields) + url += "?" + urlencode(fields) return self.urlopen(method, url, **extra_kw) - def request_encode_body(self, method, url, fields=None, headers=None, - encode_multipart=True, multipart_boundary=None, - **urlopen_kw): + def request_encode_body( + self, + method, + url, + fields=None, + headers=None, + encode_multipart=True, + multipart_boundary=None, + **urlopen_kw + ): """ Make a request using :meth:`urlopen` with the ``fields`` encoded in the body. This is useful for request methods like POST, PUT, PATCH, etc. When ``encode_multipart=True`` (default), then - :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + :func:`urllib3.encode_multipart_formdata` is used to encode the payload with the appropriate content type. Otherwise - :meth:`urllib.urlencode` is used with the + :func:`urllib.parse.urlencode` is used with the 'application/x-www-form-urlencoded' content type. Multipart encoding must be used when posting files, and it's reasonably @@ -129,22 +143,28 @@ class RequestMethods(object): if headers is None: headers = self.headers - extra_kw = {'headers': {}} + extra_kw = {"headers": {}} if fields: - if 'body' in urlopen_kw: + if "body" in urlopen_kw: raise TypeError( - "request got values for both 'fields' and 'body', can only specify one.") + "request got values for both 'fields' and 'body', can only specify one." + ) if encode_multipart: - body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) + body, content_type = encode_multipart_formdata( + fields, boundary=multipart_boundary + ) else: - body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' + body, content_type = ( + urlencode(fields), + "application/x-www-form-urlencoded", + ) - extra_kw['body'] = body - extra_kw['headers'] = {'Content-Type': content_type} + extra_kw["body"] = body + extra_kw["headers"] = {"Content-Type": content_type} - extra_kw['headers'].update(headers) + extra_kw["headers"].update(headers) extra_kw.update(urlopen_kw) return self.urlopen(method, url, **extra_kw) diff --git a/libs/common/urllib3/response.py b/libs/common/urllib3/response.py index c112690b..8f1b4fa8 100644 --- a/libs/common/urllib3/response.py +++ b/libs/common/urllib3/response.py @@ -1,29 +1,47 @@ from __future__ import absolute_import -from contextlib import contextmanager -import zlib + import io import logging -from socket import timeout as SocketTimeout +import sys +import warnings +import zlib +from contextlib import contextmanager from socket import error as SocketError +from socket import timeout as SocketTimeout +try: + try: + import brotlicffi as brotli + except ImportError: + import brotli +except ImportError: + brotli = None + +from . import util from ._collections import HTTPHeaderDict +from .connection import BaseSSLError, HTTPException from .exceptions import ( - BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError, - ResponseNotChunked, IncompleteRead, InvalidHeader + BodyNotHttplibCompatible, + DecodeError, + HTTPError, + IncompleteRead, + InvalidChunkLength, + InvalidHeader, + ProtocolError, + ReadTimeoutError, + ResponseNotChunked, + SSLError, ) -from .packages.six import string_types as basestring, PY3 -from .packages.six.moves import http_client as httplib -from .connection import HTTPException, BaseSSLError +from .packages import six from .util.response import is_fp_closed, is_response_to_head log = logging.getLogger(__name__) class DeflateDecoder(object): - def __init__(self): self._first_try = True - self._data = b'' + self._data = b"" self._obj = zlib.decompressobj() def __getattr__(self, name): @@ -60,7 +78,6 @@ class GzipDecoderState(object): class GzipDecoder(object): - def __init__(self): self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) self._state = GzipDecoderState.FIRST_MEMBER @@ -90,6 +107,25 @@ class GzipDecoder(object): self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) +if brotli is not None: + + class BrotliDecoder(object): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__(self): + self._obj = brotli.Decompressor() + if hasattr(self._obj, "decompress"): + self.decompress = self._obj.decompress + else: + self.decompress = self._obj.process + + def flush(self): + if hasattr(self._obj, "flush"): + return self._obj.flush() + return b"" + + class MultiDecoder(object): """ From RFC7231: @@ -100,7 +136,7 @@ class MultiDecoder(object): """ def __init__(self, modes): - self._decoders = [_get_decoder(m.strip()) for m in modes.split(',')] + self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] def flush(self): return self._decoders[0].flush() @@ -112,12 +148,15 @@ class MultiDecoder(object): def _get_decoder(mode): - if ',' in mode: + if "," in mode: return MultiDecoder(mode) - if mode == 'gzip': + if mode == "gzip": return GzipDecoder() + if brotli is not None and mode == "br": + return BrotliDecoder() + return DeflateDecoder() @@ -125,13 +164,13 @@ class HTTPResponse(io.IOBase): """ HTTP Response container. - Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is loaded and decoded on-demand when the ``data`` property is accessed. This class is also compatible with the Python standard library's :mod:`io` module, and can hence be treated as a readable object in the context of that framework. - Extra parameters for behaviour not present in httplib.HTTPResponse: + Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: :param preload_content: If True, the response's body will be preloaded during construction. @@ -141,7 +180,7 @@ class HTTPResponse(io.IOBase): 'content-encoding' header. :param original_response: - When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` object, it's convenient to include the original for debug purposes. It's otherwise unused. @@ -154,14 +193,31 @@ class HTTPResponse(io.IOBase): value of Content-Length header, if present. Otherwise, raise error. """ - CONTENT_DECODERS = ['gzip', 'deflate'] + CONTENT_DECODERS = ["gzip", "deflate"] + if brotli is not None: + CONTENT_DECODERS += ["br"] REDIRECT_STATUSES = [301, 302, 303, 307, 308] - def __init__(self, body='', headers=None, status=0, version=0, reason=None, - strict=0, preload_content=True, decode_content=True, - original_response=None, pool=None, connection=None, msg=None, - retries=None, enforce_content_length=False, - request_method=None, request_url=None): + def __init__( + self, + body="", + headers=None, + status=0, + version=0, + reason=None, + strict=0, + preload_content=True, + decode_content=True, + original_response=None, + pool=None, + connection=None, + msg=None, + retries=None, + enforce_content_length=False, + request_method=None, + request_url=None, + auto_close=True, + ): if isinstance(headers, HTTPHeaderDict): self.headers = headers @@ -174,6 +230,7 @@ class HTTPResponse(io.IOBase): self.decode_content = decode_content self.retries = retries self.enforce_content_length = enforce_content_length + self.auto_close = auto_close self._decoder = None self._body = None @@ -183,19 +240,19 @@ class HTTPResponse(io.IOBase): self.msg = msg self._request_url = request_url - if body and isinstance(body, (basestring, bytes)): + if body and isinstance(body, (six.string_types, bytes)): self._body = body self._pool = pool self._connection = connection - if hasattr(body, 'read'): + if hasattr(body, "read"): self._fp = body # Are we using the chunked-style of transfer encoding? self.chunked = False self.chunk_left = None - tr_enc = self.headers.get('transfer-encoding', '').lower() + tr_enc = self.headers.get("transfer-encoding", "").lower() # Don't incur the penalty of creating a list and then discarding it encodings = (enc.strip() for enc in tr_enc.split(",")) if "chunked" in encodings: @@ -217,7 +274,7 @@ class HTTPResponse(io.IOBase): location. ``False`` if not a redirect status code. """ if self.status in self.REDIRECT_STATUSES: - return self.headers.get('location') + return self.headers.get("location") return False @@ -228,9 +285,20 @@ class HTTPResponse(io.IOBase): self._pool._put_conn(self._connection) self._connection = None + def drain_conn(self): + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, SocketError, BaseSSLError, HTTPException): + pass + @property def data(self): - # For backwords-compat with earlier urllib3 0.4 and earlier. + # For backwards-compat with earlier urllib3 0.4 and earlier. if self._body: return self._body @@ -247,8 +315,8 @@ class HTTPResponse(io.IOBase): def tell(self): """ Obtain the number of bytes pulled over the wire so far. May differ from - the amount of content returned by :meth:``HTTPResponse.read`` if bytes - are encoded on the wire (e.g, compressed). + the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` + if bytes are encoded on the wire (e.g, compressed). """ return self._fp_bytes_read @@ -256,18 +324,20 @@ class HTTPResponse(io.IOBase): """ Set initial length value for Response content if available. """ - length = self.headers.get('content-length') + length = self.headers.get("content-length") if length is not None: if self.chunked: # This Response will fail with an IncompleteRead if it can't be # received as chunked. This method falls back to attempt reading # the response before raising an exception. - log.warning("Received response with both Content-Length and " - "Transfer-Encoding set. This is expressly forbidden " - "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " - "attempting to process response as Transfer-Encoding: " - "chunked.") + log.warning( + "Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked." + ) return None try: @@ -276,10 +346,12 @@ class HTTPResponse(io.IOBase): # (e.g. Content-Length: 42, 42). This line ensures the values # are all valid ints and that as long as the `set` length is 1, # all values are the same. Otherwise, the header is invalid. - lengths = set([int(val) for val in length.split(',')]) + lengths = set([int(val) for val in length.split(",")]) if len(lengths) > 1: - raise InvalidHeader("Content-Length contained multiple " - "unmatching values (%s)" % length) + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % length + ) length = lengths.pop() except ValueError: length = None @@ -295,7 +367,7 @@ class HTTPResponse(io.IOBase): status = 0 # Check for responses that shouldn't include a body - if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD': + if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": length = 0 return length @@ -306,29 +378,41 @@ class HTTPResponse(io.IOBase): """ # Note: content-encoding value should be case-insensitive, per RFC 7230 # Section 3.2 - content_encoding = self.headers.get('content-encoding', '').lower() + content_encoding = self.headers.get("content-encoding", "").lower() if self._decoder is None: if content_encoding in self.CONTENT_DECODERS: self._decoder = _get_decoder(content_encoding) - elif ',' in content_encoding: - encodings = [e.strip() for e in content_encoding.split(',') if e.strip() in self.CONTENT_DECODERS] + elif "," in content_encoding: + encodings = [ + e.strip() + for e in content_encoding.split(",") + if e.strip() in self.CONTENT_DECODERS + ] if len(encodings): self._decoder = _get_decoder(content_encoding) + DECODER_ERROR_CLASSES = (IOError, zlib.error) + if brotli is not None: + DECODER_ERROR_CLASSES += (brotli.error,) + def _decode(self, data, decode_content, flush_decoder): """ Decode the data passed in and potentially flush the decoder. """ + if not decode_content: + return data + try: - if decode_content and self._decoder: + if self._decoder: data = self._decoder.decompress(data) - except (IOError, zlib.error) as e: - content_encoding = self.headers.get('content-encoding', '').lower() + except self.DECODER_ERROR_CLASSES as e: + content_encoding = self.headers.get("content-encoding", "").lower() raise DecodeError( "Received response with content-encoding: %s, but " - "failed to decode it." % content_encoding, e) - - if flush_decoder and decode_content: + "failed to decode it." % content_encoding, + e, + ) + if flush_decoder: data += self._flush_decoder() return data @@ -339,10 +423,10 @@ class HTTPResponse(io.IOBase): being used. """ if self._decoder: - buf = self._decoder.decompress(b'') + buf = self._decoder.decompress(b"") return buf + self._decoder.flush() - return b'' + return b"" @contextmanager def _error_catcher(self): @@ -362,20 +446,19 @@ class HTTPResponse(io.IOBase): except SocketTimeout: # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but # there is yet no clean way to get at it from this context. - raise ReadTimeoutError(self._pool, None, 'Read timed out.') + raise ReadTimeoutError(self._pool, None, "Read timed out.") except BaseSSLError as e: # FIXME: Is there a better way to differentiate between SSLErrors? - if 'read operation timed out' not in str(e): # Defensive: - # This shouldn't happen but just in case we're missing an edge - # case, let's avoid swallowing SSL errors. - raise + if "read operation timed out" not in str(e): + # SSL errors related to framing/MAC get wrapped and reraised here + raise SSLError(e) - raise ReadTimeoutError(self._pool, None, 'Read timed out.') + raise ReadTimeoutError(self._pool, None, "Read timed out.") except (HTTPException, SocketError) as e: # This includes IncompleteRead. - raise ProtocolError('Connection broken: %r' % e, e) + raise ProtocolError("Connection broken: %r" % e, e) # If no exception is thrown, we should avoid cleaning up # unnecessarily. @@ -401,9 +484,57 @@ class HTTPResponse(io.IOBase): if self._original_response and self._original_response.isclosed(): self.release_conn() + def _fp_read(self, amt): + """ + Read a response with the thought that reading the number of bytes + larger than can fit in a 32-bit int at a time via SSL in some + known cases leads to an overflow error that has to be prevented + if `amt` or `self.length_remaining` indicate that a problem may + happen. + + The known cases: + * 3.8 <= CPython < 3.9.7 because of a bug + https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900. + * urllib3 injected with pyOpenSSL-backed SSL-support. + * CPython < 3.10 only when `amt` does not fit 32-bit int. + """ + assert self._fp + c_int_max = 2 ** 31 - 1 + if ( + ( + (amt and amt > c_int_max) + or (self.length_remaining and self.length_remaining > c_int_max) + ) + and not util.IS_SECURETRANSPORT + and (util.IS_PYOPENSSL or sys.version_info < (3, 10)) + ): + buffer = io.BytesIO() + # Besides `max_chunk_amt` being a maximum chunk size, it + # affects memory overhead of reading a response by this + # method in CPython. + # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum + # chunk size that does not lead to an overflow error, but + # 256 MiB is a compromise. + max_chunk_amt = 2 ** 28 + while amt is None or amt != 0: + if amt is not None: + chunk_amt = min(amt, max_chunk_amt) + amt -= chunk_amt + else: + chunk_amt = max_chunk_amt + data = self._fp.read(chunk_amt) + if not data: + break + buffer.write(data) + del data # to reduce peak memory usage by `max_chunk_amt`. + return buffer.getvalue() + else: + # StringIO doesn't like amt=None + return self._fp.read(amt) if amt is not None else self._fp.read() + def read(self, amt=None, decode_content=None, cache_content=False): """ - Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + Similar to :meth:`http.client.HTTPResponse.read`, but with two additional parameters: ``decode_content`` and ``cache_content``. :param amt: @@ -430,17 +561,17 @@ class HTTPResponse(io.IOBase): return flush_decoder = False - data = None + fp_closed = getattr(self._fp, "closed", False) with self._error_catcher(): + data = self._fp_read(amt) if not fp_closed else b"" if amt is None: - # cStringIO doesn't like amt=None - data = self._fp.read() flush_decoder = True else: cache_content = False - data = self._fp.read(amt) - if amt != 0 and not data: # Platform-specific: Buggy versions of Python. + if ( + amt != 0 and not data + ): # Platform-specific: Buggy versions of Python. # Close the connection when no data is returned # # This is redundant to what httplib/http.client _should_ @@ -450,7 +581,10 @@ class HTTPResponse(io.IOBase): # no harm in redundantly calling close. self._fp.close() flush_decoder = True - if self.enforce_content_length and self.length_remaining not in (0, None): + if self.enforce_content_length and self.length_remaining not in ( + 0, + None, + ): # This is an edge case that httplib failed to cover due # to concerns of backward compatibility. We're # addressing it here to make sure IncompleteRead is @@ -470,7 +604,7 @@ class HTTPResponse(io.IOBase): return data - def stream(self, amt=2**16, decode_content=None): + def stream(self, amt=2 ** 16, decode_content=None): """ A generator wrapper for the read() method. A call will block until ``amt`` bytes have been read from the connection or until the @@ -499,7 +633,7 @@ class HTTPResponse(io.IOBase): @classmethod def from_httplib(ResponseCls, r, **response_kw): """ - Given an :class:`httplib.HTTPResponse` instance ``r``, return a + Given an :class:`http.client.HTTPResponse` instance ``r``, return a corresponding :class:`urllib3.response.HTTPResponse` object. Remaining parameters are passed to the HTTPResponse constructor, along @@ -508,28 +642,43 @@ class HTTPResponse(io.IOBase): headers = r.msg if not isinstance(headers, HTTPHeaderDict): - if PY3: # Python 3 - headers = HTTPHeaderDict(headers.items()) - else: # Python 2 + if six.PY2: + # Python 2.7 headers = HTTPHeaderDict.from_httplib(headers) + else: + headers = HTTPHeaderDict(headers.items()) # HTTPResponse objects in Python 3 don't have a .strict attribute - strict = getattr(r, 'strict', 0) - resp = ResponseCls(body=r, - headers=headers, - status=r.status, - version=r.version, - reason=r.reason, - strict=strict, - original_response=r, - **response_kw) + strict = getattr(r, "strict", 0) + resp = ResponseCls( + body=r, + headers=headers, + status=r.status, + version=r.version, + reason=r.reason, + strict=strict, + original_response=r, + **response_kw + ) return resp - # Backwards-compatibility methods for httplib.HTTPResponse + # Backwards-compatibility methods for http.client.HTTPResponse def getheaders(self): + warnings.warn( + "HTTPResponse.getheaders() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead access HTTResponse.headers directly.", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers def getheader(self, name, default=None): + warnings.warn( + "HTTPResponse.getheader() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTResponse.headers.get(name, default).", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers.get(name, default) # Backwards compatibility for http.cookiejar @@ -544,13 +693,18 @@ class HTTPResponse(io.IOBase): if self._connection: self._connection.close() + if not self.auto_close: + io.IOBase.close(self) + @property def closed(self): - if self._fp is None: + if not self.auto_close: + return io.IOBase.closed.__get__(self) + elif self._fp is None: return True - elif hasattr(self._fp, 'isclosed'): + elif hasattr(self._fp, "isclosed"): return self._fp.isclosed() - elif hasattr(self._fp, 'closed'): + elif hasattr(self._fp, "closed"): return self._fp.closed else: return True @@ -561,11 +715,17 @@ class HTTPResponse(io.IOBase): elif hasattr(self._fp, "fileno"): return self._fp.fileno() else: - raise IOError("The file-like object this HTTPResponse is wrapped " - "around has no file descriptor") + raise IOError( + "The file-like object this HTTPResponse is wrapped " + "around has no file descriptor" + ) def flush(self): - if self._fp is not None and hasattr(self._fp, 'flush'): + if ( + self._fp is not None + and hasattr(self._fp, "flush") + and not getattr(self._fp, "closed", False) + ): return self._fp.flush() def readable(self): @@ -578,17 +738,17 @@ class HTTPResponse(io.IOBase): if len(temp) == 0: return 0 else: - b[:len(temp)] = temp + b[: len(temp)] = temp return len(temp) def supports_chunked_reads(self): """ Checks if the underlying file-like object looks like a - httplib.HTTPResponse object. We do this by testing for the fp - attribute. If it is present we assume it returns raw chunks as + :class:`http.client.HTTPResponse` object. We do this by testing for + the fp attribute. If it is present we assume it returns raw chunks as processed by read_chunked(). """ - return hasattr(self._fp, 'fp') + return hasattr(self._fp, "fp") def _update_chunk_length(self): # First, we'll figure out length of a chunk and then @@ -596,13 +756,13 @@ class HTTPResponse(io.IOBase): if self.chunk_left is not None: return line = self._fp.fp.readline() - line = line.split(b';', 1)[0] + line = line.split(b";", 1)[0] try: self.chunk_left = int(line, 16) except ValueError: # Invalid chunked protocol response, abort. self.close() - raise httplib.IncompleteRead(line) + raise InvalidChunkLength(self, line) def _handle_chunk(self, amt): returned_chunk = None @@ -645,11 +805,13 @@ class HTTPResponse(io.IOBase): if not self.chunked: raise ResponseNotChunked( "Response is not chunked. " - "Header 'transfer-encoding: chunked' is missing.") + "Header 'transfer-encoding: chunked' is missing." + ) if not self.supports_chunked_reads(): raise BodyNotHttplibCompatible( - "Body should be httplib.HTTPResponse like. " - "It should have have an fp attribute which returns raw chunks.") + "Body should be http.client.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks." + ) with self._error_catcher(): # Don't bother reading the body of a HEAD request. @@ -667,8 +829,9 @@ class HTTPResponse(io.IOBase): if self.chunk_left == 0: break chunk = self._handle_chunk(amt) - decoded = self._decode(chunk, decode_content=decode_content, - flush_decoder=False) + decoded = self._decode( + chunk, decode_content=decode_content, flush_decoder=False + ) if decoded: yield decoded @@ -686,7 +849,7 @@ class HTTPResponse(io.IOBase): if not line: # Some sites may not end with '\r\n'. break - if line == b'\r\n': + if line == b"\r\n": break # We read everything; close the "file". @@ -703,3 +866,20 @@ class HTTPResponse(io.IOBase): return self.retries.history[-1].redirect_location else: return self._request_url + + def __iter__(self): + buffer = [] + for chunk in self.stream(decode_content=True): + if b"\n" in chunk: + chunk = chunk.split(b"\n") + yield b"".join(buffer) + chunk[0] + b"\n" + for x in chunk[1:-1]: + yield x + b"\n" + if chunk[-1]: + buffer = [chunk[-1]] + else: + buffer = [] + else: + buffer.append(chunk) + if buffer: + yield b"".join(buffer) diff --git a/libs/common/urllib3/util/__init__.py b/libs/common/urllib3/util/__init__.py index 2f2770b6..4547fc52 100644 --- a/libs/common/urllib3/util/__init__.py +++ b/libs/common/urllib3/util/__init__.py @@ -1,54 +1,49 @@ from __future__ import absolute_import + # For backwards compatibility, provide imports that used to be here. from .connection import is_connection_dropped -from .request import make_headers +from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers from .response import is_fp_closed +from .retry import Retry from .ssl_ import ( - SSLContext, + ALPN_PROTOCOLS, HAS_SNI, IS_PYOPENSSL, IS_SECURETRANSPORT, + PROTOCOL_TLS, + SSLContext, assert_fingerprint, resolve_cert_reqs, resolve_ssl_version, ssl_wrap_socket, ) -from .timeout import ( - current_time, - Timeout, -) - -from .retry import Retry -from .url import ( - get_host, - parse_url, - split_first, - Url, -) -from .wait import ( - wait_for_read, - wait_for_write -) +from .timeout import Timeout, current_time +from .url import Url, get_host, parse_url, split_first +from .wait import wait_for_read, wait_for_write __all__ = ( - 'HAS_SNI', - 'IS_PYOPENSSL', - 'IS_SECURETRANSPORT', - 'SSLContext', - 'Retry', - 'Timeout', - 'Url', - 'assert_fingerprint', - 'current_time', - 'is_connection_dropped', - 'is_fp_closed', - 'get_host', - 'parse_url', - 'make_headers', - 'resolve_cert_reqs', - 'resolve_ssl_version', - 'split_first', - 'ssl_wrap_socket', - 'wait_for_read', - 'wait_for_write' + "HAS_SNI", + "IS_PYOPENSSL", + "IS_SECURETRANSPORT", + "SSLContext", + "PROTOCOL_TLS", + "ALPN_PROTOCOLS", + "Retry", + "Timeout", + "Url", + "assert_fingerprint", + "current_time", + "is_connection_dropped", + "is_fp_closed", + "get_host", + "parse_url", + "make_headers", + "resolve_cert_reqs", + "resolve_ssl_version", + "split_first", + "ssl_wrap_socket", + "wait_for_read", + "wait_for_write", + "SKIP_HEADER", + "SKIPPABLE_HEADERS", ) diff --git a/libs/common/urllib3/util/connection.py b/libs/common/urllib3/util/connection.py index 5ad70b2f..6af1138f 100644 --- a/libs/common/urllib3/util/connection.py +++ b/libs/common/urllib3/util/connection.py @@ -1,7 +1,11 @@ from __future__ import absolute_import + import socket -from .wait import NoWayToWaitForSocketError, wait_for_read + from ..contrib import _appengine_environ +from ..exceptions import LocationParseError +from ..packages import six +from .wait import NoWayToWaitForSocketError, wait_for_read def is_connection_dropped(conn): # Platform-specific @@ -9,12 +13,12 @@ def is_connection_dropped(conn): # Platform-specific Returns True if the connection is dropped and should be closed. :param conn: - :class:`httplib.HTTPConnection` object. + :class:`http.client.HTTPConnection` object. Note: For platforms like AppEngine, this will always return ``False`` to let the platform handle connection recycling transparently for us. """ - sock = getattr(conn, 'sock', False) + sock = getattr(conn, "sock", False) if sock is False: # Platform-specific: AppEngine return False if sock is None: # Connection already closed (such as by httplib). @@ -30,23 +34,27 @@ def is_connection_dropped(conn): # Platform-specific # library test suite. Added to its signature is only `socket_options`. # One additional modification is that we avoid binding to IPv6 servers # discovered in DNS if the system doesn't have IPv6 functionality. -def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, socket_options=None): +def create_connection( + address, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, + socket_options=None, +): """Connect to *address* and return the socket object. Convenience function. Connect to *address* (a 2-tuple ``(host, port)``) and return the socket object. Passing the optional *timeout* parameter will set the timeout on the socket instance before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` + global default timeout setting returned by :func:`socket.getdefaulttimeout` is used. If *source_address* is set it must be a tuple of (host, port) for the socket to bind as a source address before making the connection. An host of '' or port 0 tells the OS to use the default. """ host, port = address - if host.startswith('['): - host = host.strip('[]') + if host.startswith("["): + host = host.strip("[]") err = None # Using the value from allowed_gai_family() in the context of getaddrinfo lets @@ -54,6 +62,13 @@ def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, # The original create_connection function always returns all records. family = allowed_gai_family() + try: + host.encode("idna") + except UnicodeError: + return six.raise_from( + LocationParseError(u"'%s', label empty or too long" % host), None + ) + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res sock = None @@ -102,7 +117,7 @@ def allowed_gai_family(): def _has_ipv6(host): - """ Returns True if the system can bind an IPv6 address. """ + """Returns True if the system can bind an IPv6 address.""" sock = None has_ipv6 = False @@ -117,7 +132,7 @@ def _has_ipv6(host): # has_ipv6 returns true if cPython was compiled with IPv6 support. # It does not tell us if the system has IPv6 support enabled. To # determine that we must bind to an IPv6 address. - # https://github.com/shazow/urllib3/pull/611 + # https://github.com/urllib3/urllib3/pull/611 # https://bugs.python.org/issue658327 try: sock = socket.socket(socket.AF_INET6) @@ -131,4 +146,4 @@ def _has_ipv6(host): return has_ipv6 -HAS_IPV6 = _has_ipv6('::1') +HAS_IPV6 = _has_ipv6("::1") diff --git a/libs/common/urllib3/util/proxy.py b/libs/common/urllib3/util/proxy.py new file mode 100644 index 00000000..2199cc7b --- /dev/null +++ b/libs/common/urllib3/util/proxy.py @@ -0,0 +1,57 @@ +from .ssl_ import create_urllib3_context, resolve_cert_reqs, resolve_ssl_version + + +def connection_requires_http_tunnel( + proxy_url=None, proxy_config=None, destination_scheme=None +): + """ + Returns True if the connection requires an HTTP CONNECT through the proxy. + + :param URL proxy_url: + URL of the proxy. + :param ProxyConfig proxy_config: + Proxy configuration from poolmanager.py + :param str destination_scheme: + The scheme of the destination. (i.e https, http, etc) + """ + # If we're not using a proxy, no way to use a tunnel. + if proxy_url is None: + return False + + # HTTP destinations never require tunneling, we always forward. + if destination_scheme == "http": + return False + + # Support for forwarding with HTTPS proxies and HTTPS destinations. + if ( + proxy_url.scheme == "https" + and proxy_config + and proxy_config.use_forwarding_for_https + ): + return False + + # Otherwise always use a tunnel. + return True + + +def create_proxy_ssl_context( + ssl_version, cert_reqs, ca_certs=None, ca_cert_dir=None, ca_cert_data=None +): + """ + Generates a default proxy ssl context if one hasn't been provided by the + user. + """ + ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(ssl_version), + cert_reqs=resolve_cert_reqs(cert_reqs), + ) + + if ( + not ca_certs + and not ca_cert_dir + and not ca_cert_data + and hasattr(ssl_context, "load_default_certs") + ): + ssl_context.load_default_certs() + + return ssl_context diff --git a/libs/common/urllib3/util/queue.py b/libs/common/urllib3/util/queue.py index d3d379a1..41784104 100644 --- a/libs/common/urllib3/util/queue.py +++ b/libs/common/urllib3/util/queue.py @@ -1,4 +1,5 @@ import collections + from ..packages import six from ..packages.six.moves import queue diff --git a/libs/common/urllib3/util/request.py b/libs/common/urllib3/util/request.py index 3ddfcd55..b574b081 100644 --- a/libs/common/urllib3/util/request.py +++ b/libs/common/urllib3/util/request.py @@ -1,15 +1,39 @@ from __future__ import absolute_import + from base64 import b64encode -from ..packages.six import b, integer_types from ..exceptions import UnrewindableBodyError +from ..packages.six import b, integer_types + +# Pass as a value within ``headers`` to skip +# emitting some HTTP headers that are added automatically. +# The only headers that are supported are ``Accept-Encoding``, +# ``Host``, and ``User-Agent``. +SKIP_HEADER = "@@@SKIP_HEADER@@@" +SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) + +ACCEPT_ENCODING = "gzip,deflate" +try: + try: + import brotlicffi as _unused_module_brotli # noqa: F401 + except ImportError: + import brotli as _unused_module_brotli # noqa: F401 +except ImportError: + pass +else: + ACCEPT_ENCODING += ",br" -ACCEPT_ENCODING = 'gzip,deflate' _FAILEDTELL = object() -def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, - basic_auth=None, proxy_basic_auth=None, disable_cache=None): +def make_headers( + keep_alive=None, + accept_encoding=None, + user_agent=None, + basic_auth=None, + proxy_basic_auth=None, + disable_cache=None, +): """ Shortcuts for generating request headers. @@ -49,27 +73,27 @@ def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, if isinstance(accept_encoding, str): pass elif isinstance(accept_encoding, list): - accept_encoding = ','.join(accept_encoding) + accept_encoding = ",".join(accept_encoding) else: accept_encoding = ACCEPT_ENCODING - headers['accept-encoding'] = accept_encoding + headers["accept-encoding"] = accept_encoding if user_agent: - headers['user-agent'] = user_agent + headers["user-agent"] = user_agent if keep_alive: - headers['connection'] = 'keep-alive' + headers["connection"] = "keep-alive" if basic_auth: - headers['authorization'] = 'Basic ' + \ - b64encode(b(basic_auth)).decode('utf-8') + headers["authorization"] = "Basic " + b64encode(b(basic_auth)).decode("utf-8") if proxy_basic_auth: - headers['proxy-authorization'] = 'Basic ' + \ - b64encode(b(proxy_basic_auth)).decode('utf-8') + headers["proxy-authorization"] = "Basic " + b64encode( + b(proxy_basic_auth) + ).decode("utf-8") if disable_cache: - headers['cache-control'] = 'no-cache' + headers["cache-control"] = "no-cache" return headers @@ -81,7 +105,7 @@ def set_file_position(body, pos): """ if pos is not None: rewind_body(body, pos) - elif getattr(body, 'tell', None) is not None: + elif getattr(body, "tell", None) is not None: try: pos = body.tell() except (IOError, OSError): @@ -103,16 +127,20 @@ def rewind_body(body, body_pos): :param int pos: Position to seek to in file. """ - body_seek = getattr(body, 'seek', None) + body_seek = getattr(body, "seek", None) if body_seek is not None and isinstance(body_pos, integer_types): try: body_seek(body_pos) except (IOError, OSError): - raise UnrewindableBodyError("An error occurred when rewinding request " - "body for redirect/retry.") + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect/retry." + ) elif body_pos is _FAILEDTELL: - raise UnrewindableBodyError("Unable to record file position for rewinding " - "request body during a redirect/retry.") + raise UnrewindableBodyError( + "Unable to record file position for rewinding " + "request body during a redirect/retry." + ) else: - raise ValueError("body_pos must be of type integer, " - "instead it was %s." % type(body_pos)) + raise ValueError( + "body_pos must be of type integer, instead it was %s." % type(body_pos) + ) diff --git a/libs/common/urllib3/util/response.py b/libs/common/urllib3/util/response.py index 3d548648..5ea609cc 100644 --- a/libs/common/urllib3/util/response.py +++ b/libs/common/urllib3/util/response.py @@ -1,7 +1,9 @@ from __future__ import absolute_import -from ..packages.six.moves import http_client as httplib + +from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect from ..exceptions import HeaderParsingError +from ..packages.six.moves import http_client as httplib def is_fp_closed(obj): @@ -42,8 +44,7 @@ def assert_header_parsing(headers): Only works on Python 3. - :param headers: Headers to verify. - :type headers: `httplib.HTTPMessage`. + :param http.client.HTTPMessage headers: Headers to verify. :raises urllib3.exceptions.HeaderParsingError: If parsing errors are found. @@ -52,11 +53,10 @@ def assert_header_parsing(headers): # This will fail silently if we pass in the wrong kind of parameter. # To make debugging easier add an explicit check. if not isinstance(headers, httplib.HTTPMessage): - raise TypeError('expected httplib.Message, got {0}.'.format( - type(headers))) + raise TypeError("expected httplib.Message, got {0}.".format(type(headers))) - defects = getattr(headers, 'defects', None) - get_payload = getattr(headers, 'get_payload', None) + defects = getattr(headers, "defects", None) + get_payload = getattr(headers, "get_payload", None) unparsed_data = None if get_payload: @@ -67,6 +67,25 @@ def assert_header_parsing(headers): if isinstance(payload, (bytes, str)): unparsed_data = payload + if defects: + # httplib is assuming a response body is available + # when parsing headers even when httplib only sends + # header data to parse_headers() This results in + # defects on multipart responses in particular. + # See: https://github.com/urllib3/urllib3/issues/800 + + # So we ignore the following defects: + # - StartBoundaryNotFoundDefect: + # The claimed start boundary was never found. + # - MultipartInvariantViolationDefect: + # A message claimed to be a multipart but no subparts were found. + defects = [ + defect + for defect in defects + if not isinstance( + defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) + ) + ] if defects or unparsed_data: raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) @@ -77,11 +96,12 @@ def is_response_to_head(response): Checks whether the request of a response has been a HEAD-request. Handles the quirks of AppEngine. - :param conn: - :type conn: :class:`httplib.HTTPResponse` + :param http.client.HTTPResponse response: + Response to check if the originating request + used 'HEAD' as a method. """ # FIXME: Can we do this somehow without accessing private httplib _method? method = response._method if isinstance(method, int): # Platform-specific: Appengine return method == 3 - return method.upper() == 'HEAD' + return method.upper() == "HEAD" diff --git a/libs/common/urllib3/util/retry.py b/libs/common/urllib3/util/retry.py index e7d0abd6..2490d5e5 100644 --- a/libs/common/urllib3/util/retry.py +++ b/libs/common/urllib3/util/retry.py @@ -1,32 +1,96 @@ from __future__ import absolute_import -import time + +import email import logging +import re +import time +import warnings from collections import namedtuple from itertools import takewhile -import email -import re from ..exceptions import ( ConnectTimeoutError, + InvalidHeader, MaxRetryError, ProtocolError, + ProxyError, ReadTimeoutError, ResponseError, - InvalidHeader, ) from ..packages import six - log = logging.getLogger(__name__) # Data structure for representing the metadata of requests that result in a retry. -RequestHistory = namedtuple('RequestHistory', ["method", "url", "error", - "status", "redirect_location"]) +RequestHistory = namedtuple( + "RequestHistory", ["method", "url", "error", "status", "redirect_location"] +) +# TODO: In v2 we can remove this sentinel and metaclass with deprecated options. +_Default = object() + + +class _RetryMeta(type): + @property + def DEFAULT_METHOD_WHITELIST(cls): + warnings.warn( + "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", + DeprecationWarning, + ) + return cls.DEFAULT_ALLOWED_METHODS + + @DEFAULT_METHOD_WHITELIST.setter + def DEFAULT_METHOD_WHITELIST(cls, value): + warnings.warn( + "Using 'Retry.DEFAULT_METHOD_WHITELIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_ALLOWED_METHODS' instead", + DeprecationWarning, + ) + cls.DEFAULT_ALLOWED_METHODS = value + + @property + def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls): + warnings.warn( + "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", + DeprecationWarning, + ) + return cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT + + @DEFAULT_REDIRECT_HEADERS_BLACKLIST.setter + def DEFAULT_REDIRECT_HEADERS_BLACKLIST(cls, value): + warnings.warn( + "Using 'Retry.DEFAULT_REDIRECT_HEADERS_BLACKLIST' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_REMOVE_HEADERS_ON_REDIRECT' instead", + DeprecationWarning, + ) + cls.DEFAULT_REMOVE_HEADERS_ON_REDIRECT = value + + @property + def BACKOFF_MAX(cls): + warnings.warn( + "Using 'Retry.BACKOFF_MAX' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", + DeprecationWarning, + ) + return cls.DEFAULT_BACKOFF_MAX + + @BACKOFF_MAX.setter + def BACKOFF_MAX(cls, value): + warnings.warn( + "Using 'Retry.BACKOFF_MAX' is deprecated and " + "will be removed in v2.0. Use 'Retry.DEFAULT_BACKOFF_MAX' instead", + DeprecationWarning, + ) + cls.DEFAULT_BACKOFF_MAX = value + + +@six.add_metaclass(_RetryMeta) class Retry(object): - """ Retry configuration. + """Retry configuration. Each retry attempt will create a new Retry object with updated values, so they can be safely reused. @@ -52,8 +116,7 @@ class Retry(object): Total number of retries to allow. Takes precedence over other counts. Set to ``None`` to remove this constraint and fall back on other - counts. It's a good idea to set this to some sensibly-high value to - account for unexpected edge cases and avoid infinite retry loops. + counts. Set to ``0`` to fail on the first retry. @@ -94,18 +157,35 @@ class Retry(object): Set to ``0`` to fail on the first retry of this type. - :param iterable method_whitelist: + :param int other: + How many times to retry on other errors. + + Other errors are errors that are not connect, read, redirect or status errors. + These errors might be raised after the request was sent to the server, so the + request might have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + If ``total`` is not set, it's a good idea to set this to 0 to account + for unexpected edge cases and avoid infinite retry loops. + + :param iterable allowed_methods: Set of uppercased HTTP method verbs that we should retry on. By default, we only retry on methods which are considered to be idempotent (multiple requests with the same parameters end with the - same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. Set to a ``False`` value to retry on any verb. + .. warning:: + + Previously this parameter was named ``method_whitelist``, that + usage is deprecated in v1.26.0 and will be removed in v2.0. + :param iterable status_forcelist: A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``method_whitelist`` + A retry is initiated if the request method is in ``allowed_methods`` and the response status code is in ``status_forcelist``. By default, this is disabled with ``None``. @@ -119,7 +199,7 @@ class Retry(object): seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`Retry.BACKOFF_MAX`. + than :attr:`Retry.DEFAULT_BACKOFF_MAX`. By default, backoff is disabled (set to 0). @@ -146,26 +226,64 @@ class Retry(object): request. """ - DEFAULT_METHOD_WHITELIST = frozenset([ - 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + #: Default methods to be used for ``allowed_methods`` + DEFAULT_ALLOWED_METHODS = frozenset( + ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] + ) + #: Default status codes to be used for ``status_forcelist`` RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) - DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) + #: Default headers to be used for ``remove_headers_on_redirect`` + DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset(["Authorization"]) #: Maximum backoff time. - BACKOFF_MAX = 120 + DEFAULT_BACKOFF_MAX = 120 - def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, - method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, - backoff_factor=0, raise_on_redirect=True, raise_on_status=True, - history=None, respect_retry_after_header=True, - remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): + def __init__( + self, + total=10, + connect=None, + read=None, + redirect=None, + status=None, + other=None, + allowed_methods=_Default, + status_forcelist=None, + backoff_factor=0, + raise_on_redirect=True, + raise_on_status=True, + history=None, + respect_retry_after_header=True, + remove_headers_on_redirect=_Default, + # TODO: Deprecated, remove in v2.0 + method_whitelist=_Default, + ): + + if method_whitelist is not _Default: + if allowed_methods is not _Default: + raise ValueError( + "Using both 'allowed_methods' and " + "'method_whitelist' together is not allowed. " + "Instead only use 'allowed_methods'" + ) + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + stacklevel=2, + ) + allowed_methods = method_whitelist + if allowed_methods is _Default: + allowed_methods = self.DEFAULT_ALLOWED_METHODS + if remove_headers_on_redirect is _Default: + remove_headers_on_redirect = self.DEFAULT_REMOVE_HEADERS_ON_REDIRECT self.total = total self.connect = connect self.read = read self.status = status + self.other = other if redirect is False or total is False: redirect = 0 @@ -173,32 +291,55 @@ class Retry(object): self.redirect = redirect self.status_forcelist = status_forcelist or set() - self.method_whitelist = method_whitelist + self.allowed_methods = allowed_methods self.backoff_factor = backoff_factor self.raise_on_redirect = raise_on_redirect self.raise_on_status = raise_on_status self.history = history or tuple() self.respect_retry_after_header = respect_retry_after_header - self.remove_headers_on_redirect = remove_headers_on_redirect + self.remove_headers_on_redirect = frozenset( + [h.lower() for h in remove_headers_on_redirect] + ) def new(self, **kw): params = dict( total=self.total, - connect=self.connect, read=self.read, redirect=self.redirect, status=self.status, - method_whitelist=self.method_whitelist, + connect=self.connect, + read=self.read, + redirect=self.redirect, + status=self.status, + other=self.other, status_forcelist=self.status_forcelist, backoff_factor=self.backoff_factor, raise_on_redirect=self.raise_on_redirect, raise_on_status=self.raise_on_status, history=self.history, - remove_headers_on_redirect=self.remove_headers_on_redirect + remove_headers_on_redirect=self.remove_headers_on_redirect, + respect_retry_after_header=self.respect_retry_after_header, ) + + # TODO: If already given in **kw we use what's given to us + # If not given we need to figure out what to pass. We decide + # based on whether our class has the 'method_whitelist' property + # and if so we pass the deprecated 'method_whitelist' otherwise + # we use 'allowed_methods'. Remove in v2.0 + if "method_whitelist" not in kw and "allowed_methods" not in kw: + if "method_whitelist" in self.__dict__: + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + params["method_whitelist"] = self.allowed_methods + else: + params["allowed_methods"] = self.allowed_methods + params.update(kw) return type(self)(**params) @classmethod def from_int(cls, retries, redirect=True, default=None): - """ Backwards-compatibility for the old retries format.""" + """Backwards-compatibility for the old retries format.""" if retries is None: retries = default if default is not None else cls.DEFAULT @@ -211,28 +352,38 @@ class Retry(object): return new_retries def get_backoff_time(self): - """ Formula for computing the current backoff + """Formula for computing the current backoff :rtype: float """ # We want to consider only the last consecutive errors sequence (Ignore redirects). - consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None, - reversed(self.history)))) + consecutive_errors_len = len( + list( + takewhile(lambda x: x.redirect_location is None, reversed(self.history)) + ) + ) if consecutive_errors_len <= 1: return 0 backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) - return min(self.BACKOFF_MAX, backoff_value) + return min(self.DEFAULT_BACKOFF_MAX, backoff_value) def parse_retry_after(self, retry_after): # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 if re.match(r"^\s*[0-9]+\s*$", retry_after): seconds = int(retry_after) else: - retry_date_tuple = email.utils.parsedate(retry_after) + retry_date_tuple = email.utils.parsedate_tz(retry_after) if retry_date_tuple is None: raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) - retry_date = time.mktime(retry_date_tuple) + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) seconds = retry_date - time.time() if seconds < 0: @@ -241,9 +392,9 @@ class Retry(object): return seconds def get_retry_after(self, response): - """ Get the value of Retry-After in seconds. """ + """Get the value of Retry-After in seconds.""" - retry_after = response.getheader("Retry-After") + retry_after = response.headers.get("Retry-After") if retry_after is None: return None @@ -265,7 +416,7 @@ class Retry(object): time.sleep(backoff) def sleep(self, response=None): - """ Sleep between retry attempts. + """Sleep between retry attempts. This method will respect a server's ``Retry-After`` response header and sleep the duration of the time requested. If that is not present, it @@ -273,7 +424,7 @@ class Retry(object): this method will return immediately. """ - if response: + if self.respect_retry_after_header and response: slept = self.sleep_for_retry(response) if slept: return @@ -281,28 +432,41 @@ class Retry(object): self._sleep_backoff() def _is_connection_error(self, err): - """ Errors when we're fairly sure that the server did not receive the + """Errors when we're fairly sure that the server did not receive the request, so it should be safe to retry. """ + if isinstance(err, ProxyError): + err = err.original_error return isinstance(err, ConnectTimeoutError) def _is_read_error(self, err): - """ Errors that occur after the request has been started, so we should + """Errors that occur after the request has been started, so we should assume that the server began processing it. """ return isinstance(err, (ReadTimeoutError, ProtocolError)) def _is_method_retryable(self, method): - """ Checks if a given HTTP method should be retried upon, depending if - it is included on the method whitelist. + """Checks if a given HTTP method should be retried upon, depending if + it is included in the allowed_methods """ - if self.method_whitelist and method.upper() not in self.method_whitelist: - return False + # TODO: For now favor if the Retry implementation sets its own method_whitelist + # property outside of our constructor to avoid breaking custom implementations. + if "method_whitelist" in self.__dict__: + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + allowed_methods = self.method_whitelist + else: + allowed_methods = self.allowed_methods + if allowed_methods and method.upper() not in allowed_methods: + return False return True def is_retry(self, method, status_code, has_retry_after=False): - """ Is this method/status code retryable? (Based on whitelists and control + """Is this method/status code retryable? (Based on allowlists and control variables such as the number of total retries to allow, whether to respect the Retry-After header, whether this header is present, and whether the returned status code is on the list of status codes to @@ -314,21 +478,39 @@ class Retry(object): if self.status_forcelist and status_code in self.status_forcelist: return True - return (self.total and self.respect_retry_after_header and - has_retry_after and (status_code in self.RETRY_AFTER_STATUS_CODES)) + return ( + self.total + and self.respect_retry_after_header + and has_retry_after + and (status_code in self.RETRY_AFTER_STATUS_CODES) + ) def is_exhausted(self): - """ Are we out of retries? """ - retry_counts = (self.total, self.connect, self.read, self.redirect, self.status) + """Are we out of retries?""" + retry_counts = ( + self.total, + self.connect, + self.read, + self.redirect, + self.status, + self.other, + ) retry_counts = list(filter(None, retry_counts)) if not retry_counts: return False return min(retry_counts) < 0 - def increment(self, method=None, url=None, response=None, error=None, - _pool=None, _stacktrace=None): - """ Return a new Retry object with incremented retry counters. + def increment( + self, + method=None, + url=None, + response=None, + error=None, + _pool=None, + _stacktrace=None, + ): + """Return a new Retry object with incremented retry counters. :param response: A response object, or None, if the server did not return a response. @@ -350,7 +532,8 @@ class Retry(object): read = self.read redirect = self.redirect status_count = self.status - cause = 'unknown' + other = self.other + cause = "unknown" status = None redirect_location = None @@ -368,31 +551,42 @@ class Retry(object): elif read is not None: read -= 1 + elif error: + # Other retry? + if other is not None: + other -= 1 + elif response and response.get_redirect_location(): # Redirect retry? if redirect is not None: redirect -= 1 - cause = 'too many redirects' + cause = "too many redirects" redirect_location = response.get_redirect_location() status = response.status else: # Incrementing because of a server error like a 500 in - # status_forcelist and a the given method is in the whitelist + # status_forcelist and the given method is in the allowed_methods cause = ResponseError.GENERIC_ERROR if response and response.status: if status_count is not None: status_count -= 1 - cause = ResponseError.SPECIFIC_ERROR.format( - status_code=response.status) + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) status = response.status - history = self.history + (RequestHistory(method, url, error, status, redirect_location),) + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) new_retry = self.new( total=total, - connect=connect, read=read, redirect=redirect, status=status_count, - history=history) + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) if new_retry.is_exhausted(): raise MaxRetryError(_pool, url, error or ResponseError(cause)) @@ -402,9 +596,24 @@ class Retry(object): return new_retry def __repr__(self): - return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' - 'read={self.read}, redirect={self.redirect}, status={self.status})').format( - cls=type(self), self=self) + return ( + "{cls.__name__}(total={self.total}, connect={self.connect}, " + "read={self.read}, redirect={self.redirect}, status={self.status})" + ).format(cls=type(self), self=self) + + def __getattr__(self, item): + if item == "method_whitelist": + # TODO: Remove this deprecated alias in v2.0 + warnings.warn( + "Using 'method_whitelist' with Retry is deprecated and " + "will be removed in v2.0. Use 'allowed_methods' instead", + DeprecationWarning, + ) + return self.allowed_methods + try: + return getattr(super(Retry, self), item) + except AttributeError: + return getattr(Retry, item) # For backwards compatibility (equivalent to pre-v1.9): diff --git a/libs/common/urllib3/util/ssl_.py b/libs/common/urllib3/util/ssl_.py index 64ea192a..8f867812 100644 --- a/libs/common/urllib3/util/ssl_.py +++ b/libs/common/urllib3/util/ssl_.py @@ -1,27 +1,30 @@ from __future__ import absolute_import -import errno -import warnings -import hmac -import socket +import hmac +import os +import sys +import warnings from binascii import hexlify, unhexlify from hashlib import md5, sha1, sha256 -from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning +from ..exceptions import ( + InsecurePlatformWarning, + ProxySchemeUnsupported, + SNIMissingWarning, + SSLError, +) from ..packages import six - +from .url import BRACELESS_IPV6_ADDRZ_RE, IPV4_RE SSLContext = None +SSLTransport = None HAS_SNI = False IS_PYOPENSSL = False IS_SECURETRANSPORT = False +ALPN_PROTOCOLS = ["http/1.1"] # Maps the length of a digest to a possible hash function producing this digest -HASHFUNC_MAP = { - 32: md5, - 40: sha1, - 64: sha256, -} +HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} def _const_compare_digest_backport(a, b): @@ -32,47 +35,59 @@ def _const_compare_digest_backport(a, b): Returns True if the digests match, and False otherwise. """ result = abs(len(a) - len(b)) - for l, r in zip(bytearray(a), bytearray(b)): - result |= l ^ r + for left, right in zip(bytearray(a), bytearray(b)): + result |= left ^ right return result == 0 -_const_compare_digest = getattr(hmac, 'compare_digest', - _const_compare_digest_backport) - +_const_compare_digest = getattr(hmac, "compare_digest", _const_compare_digest_backport) try: # Test for SSL features import ssl - from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 + from ssl import CERT_REQUIRED, wrap_socket +except ImportError: + pass + +try: from ssl import HAS_SNI # Has SNI? except ImportError: pass +try: + from .ssltransport import SSLTransport +except ImportError: + pass + + +try: # Platform-specific: Python 3.6 + from ssl import PROTOCOL_TLS + + PROTOCOL_SSLv23 = PROTOCOL_TLS +except ImportError: + try: + from ssl import PROTOCOL_SSLv23 as PROTOCOL_TLS + + PROTOCOL_SSLv23 = PROTOCOL_TLS + except ImportError: + PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 try: - from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION + from ssl import PROTOCOL_TLS_CLIENT +except ImportError: + PROTOCOL_TLS_CLIENT = PROTOCOL_TLS + + +try: + from ssl import OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3 except ImportError: OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 OP_NO_COMPRESSION = 0x20000 -# Python 2.7 doesn't have inet_pton on non-Linux so we fallback on inet_aton in -# those cases. This means that we can only detect IPv4 addresses in this case. -if hasattr(socket, 'inet_pton'): - inet_pton = socket.inet_pton -else: - # Maybe we can use ipaddress if the user has urllib3[secure]? - try: - import ipaddress - - def inet_pton(_, host): - if isinstance(host, bytes): - host = host.decode('ascii') - return ipaddress.ip_address(host) - - except ImportError: # Platform-specific: Non-Linux - def inet_pton(_, host): - return socket.inet_aton(host) +try: # OP_NO_TICKET was added in Python 3.6 + from ssl import OP_NO_TICKET +except ImportError: + OP_NO_TICKET = 0x4000 # A secure default. @@ -83,36 +98,37 @@ else: # - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ # # The general intent is: -# - Prefer TLS 1.3 cipher suites # - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), # - prefer ECDHE over DHE for better performance, # - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and # security, # - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, -# - disable NULL authentication, MD5 MACs and DSS for security reasons. -DEFAULT_CIPHERS = ':'.join([ - 'TLS13-AES-256-GCM-SHA384', - 'TLS13-CHACHA20-POLY1305-SHA256', - 'TLS13-AES-128-GCM-SHA256', - 'ECDH+AESGCM', - 'ECDH+CHACHA20', - 'DH+AESGCM', - 'DH+CHACHA20', - 'ECDH+AES256', - 'DH+AES256', - 'ECDH+AES128', - 'DH+AES', - 'RSA+AESGCM', - 'RSA+AES', - '!aNULL', - '!eNULL', - '!MD5', -]) +# - disable NULL authentication, MD5 MACs, DSS, and other +# insecure ciphers for security reasons. +# - NOTE: TLS 1.3 cipher suites are managed through a different interface +# not exposed by CPython (yet!) and are enabled by default if they're available. +DEFAULT_CIPHERS = ":".join( + [ + "ECDHE+AESGCM", + "ECDHE+CHACHA20", + "DHE+AESGCM", + "DHE+CHACHA20", + "ECDH+AESGCM", + "DH+AESGCM", + "ECDH+AES", + "DH+AES", + "RSA+AESGCM", + "RSA+AES", + "!aNULL", + "!eNULL", + "!MD5", + "!DSS", + ] +) try: from ssl import SSLContext # Modern SSL? except ImportError: - import sys class SSLContext(object): # Platform-specific: Python 2 def __init__(self, protocol_version): @@ -130,32 +146,35 @@ except ImportError: self.certfile = certfile self.keyfile = keyfile - def load_verify_locations(self, cafile=None, capath=None): + def load_verify_locations(self, cafile=None, capath=None, cadata=None): self.ca_certs = cafile if capath is not None: raise SSLError("CA directories not supported in older Pythons") + if cadata is not None: + raise SSLError("CA data not supported in older Pythons") + def set_ciphers(self, cipher_suite): self.ciphers = cipher_suite def wrap_socket(self, socket, server_hostname=None, server_side=False): warnings.warn( - 'A true SSLContext object is not available. This prevents ' - 'urllib3 from configuring SSL appropriately and may cause ' - 'certain SSL connections to fail. You can upgrade to a newer ' - 'version of Python to solve this. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings', - InsecurePlatformWarning + "A true SSLContext object is not available. This prevents " + "urllib3 from configuring SSL appropriately and may cause " + "certain SSL connections to fail. You can upgrade to a newer " + "version of Python to solve this. For more information, see " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#ssl-warnings", + InsecurePlatformWarning, ) kwargs = { - 'keyfile': self.keyfile, - 'certfile': self.certfile, - 'ca_certs': self.ca_certs, - 'cert_reqs': self.verify_mode, - 'ssl_version': self.protocol, - 'server_side': server_side, + "keyfile": self.keyfile, + "certfile": self.certfile, + "ca_certs": self.ca_certs, + "cert_reqs": self.verify_mode, + "ssl_version": self.protocol, + "server_side": server_side, } return wrap_socket(socket, ciphers=self.ciphers, **kwargs) @@ -170,12 +189,11 @@ def assert_fingerprint(cert, fingerprint): Fingerprint as string of hexdigits, can be interspersed by colons. """ - fingerprint = fingerprint.replace(':', '').lower() + fingerprint = fingerprint.replace(":", "").lower() digest_length = len(fingerprint) hashfunc = HASHFUNC_MAP.get(digest_length) if not hashfunc: - raise SSLError( - 'Fingerprint of invalid length: {0}'.format(fingerprint)) + raise SSLError("Fingerprint of invalid length: {0}".format(fingerprint)) # We need encode() here for py32; works on py2 and p33. fingerprint_bytes = unhexlify(fingerprint.encode()) @@ -183,15 +201,18 @@ def assert_fingerprint(cert, fingerprint): cert_digest = hashfunc(cert).digest() if not _const_compare_digest(cert_digest, fingerprint_bytes): - raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' - .format(fingerprint, hexlify(cert_digest))) + raise SSLError( + 'Fingerprints did not match. Expected "{0}", got "{1}".'.format( + fingerprint, hexlify(cert_digest) + ) + ) def resolve_cert_reqs(candidate): """ Resolves the argument to a numeric constant, which can be passed to the wrap_socket function/method from the ssl module. - Defaults to :data:`ssl.CERT_NONE`. + Defaults to :data:`ssl.CERT_REQUIRED`. If given a string it is assumed to be the name of the constant in the :mod:`ssl` module or its abbreviation. (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. @@ -199,12 +220,12 @@ def resolve_cert_reqs(candidate): constant which can directly be passed to wrap_socket. """ if candidate is None: - return CERT_NONE + return CERT_REQUIRED if isinstance(candidate, str): res = getattr(ssl, candidate, None) if res is None: - res = getattr(ssl, 'CERT_' + candidate) + res = getattr(ssl, "CERT_" + candidate) return res return candidate @@ -215,19 +236,20 @@ def resolve_ssl_version(candidate): like resolve_cert_reqs """ if candidate is None: - return PROTOCOL_SSLv23 + return PROTOCOL_TLS if isinstance(candidate, str): res = getattr(ssl, candidate, None) if res is None: - res = getattr(ssl, 'PROTOCOL_' + candidate) + res = getattr(ssl, "PROTOCOL_" + candidate) return res return candidate -def create_urllib3_context(ssl_version=None, cert_reqs=None, - options=None, ciphers=None): +def create_urllib3_context( + ssl_version=None, cert_reqs=None, options=None, ciphers=None +): """All arguments have the same meaning as ``ssl_wrap_socket``. By default, this function does a lot of the same work that @@ -254,14 +276,18 @@ def create_urllib3_context(ssl_version=None, cert_reqs=None, ``ssl.CERT_REQUIRED``. :param options: Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, - ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. :param ciphers: Which cipher suites to allow the server to select. :returns: Constructed SSLContext object with specified options :rtype: SSLContext """ - context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) + # PROTOCOL_TLS is deprecated in Python 3.10 + if not ssl_version or ssl_version == PROTOCOL_TLS: + ssl_version = PROTOCOL_TLS_CLIENT + + context = SSLContext(ssl_version) context.set_ciphers(ciphers or DEFAULT_CIPHERS) @@ -277,21 +303,70 @@ def create_urllib3_context(ssl_version=None, cert_reqs=None, # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ # (issue #309) options |= OP_NO_COMPRESSION + # TLSv1.2 only. Unless set explicitly, do not request tickets. + # This may save some bandwidth on wire, and although the ticket is encrypted, + # there is a risk associated with it being on wire, + # if the server is not rotating its ticketing keys properly. + options |= OP_NO_TICKET context.options |= options - context.verify_mode = cert_reqs - if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 - # We do our own verification, including fingerprints and alternative - # hostnames. So disable it here - context.check_hostname = False + # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is + # necessary for conditional client cert authentication with TLS 1.3. + # The attribute is None for OpenSSL <= 1.1.0 or does not exist in older + # versions of Python. We only enable on Python 3.7.4+ or if certificate + # verification is enabled to work around Python issue #37428 + # See: https://bugs.python.org/issue37428 + if (cert_reqs == ssl.CERT_REQUIRED or sys.version_info >= (3, 7, 4)) and getattr( + context, "post_handshake_auth", None + ) is not None: + context.post_handshake_auth = True + + def disable_check_hostname(): + if ( + getattr(context, "check_hostname", None) is not None + ): # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + + # The order of the below lines setting verify_mode and check_hostname + # matter due to safe-guards SSLContext has to prevent an SSLContext with + # check_hostname=True, verify_mode=NONE/OPTIONAL. This is made even more + # complex because we don't know whether PROTOCOL_TLS_CLIENT will be used + # or not so we don't know the initial state of the freshly created SSLContext. + if cert_reqs == ssl.CERT_REQUIRED: + context.verify_mode = cert_reqs + disable_check_hostname() + else: + disable_check_hostname() + context.verify_mode = cert_reqs + + # Enable logging of TLS session keys via defacto standard environment variable + # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. + if hasattr(context, "keylog_filename"): + sslkeylogfile = os.environ.get("SSLKEYLOGFILE") + if sslkeylogfile: + context.keylog_filename = sslkeylogfile + return context -def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None, ciphers=None, ssl_context=None, - ca_cert_dir=None): +def ssl_wrap_socket( + sock, + keyfile=None, + certfile=None, + cert_reqs=None, + ca_certs=None, + server_hostname=None, + ssl_version=None, + ciphers=None, + ssl_context=None, + ca_cert_dir=None, + key_password=None, + ca_cert_data=None, + tls_in_tls=False, +): """ All arguments except for server_hostname, ssl_context, and ca_cert_dir have the same meaning as they do when using :func:`ssl.wrap_socket`. @@ -307,75 +382,114 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, A directory containing CA certificates in multiple separate files, as supported by OpenSSL's -CApath flag or the capath argument to SSLContext.load_verify_locations(). + :param key_password: + Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() + :param tls_in_tls: + Use SSLTransport to wrap the existing socket. """ context = ssl_context if context is None: # Note: This branch of code and all the variables in it are no longer # used by urllib3 itself. We should consider deprecating and removing # this code. - context = create_urllib3_context(ssl_version, cert_reqs, - ciphers=ciphers) + context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) - if ca_certs or ca_cert_dir: + if ca_certs or ca_cert_dir or ca_cert_data: try: - context.load_verify_locations(ca_certs, ca_cert_dir) - except IOError as e: # Platform-specific: Python 2.7 + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) + except (IOError, OSError) as e: raise SSLError(e) - # Py33 raises FileNotFoundError which subclasses OSError - # These are not equivalent unless we check the errno attribute - except OSError as e: # Platform-specific: Python 3.3 and beyond - if e.errno == errno.ENOENT: - raise SSLError(e) - raise - elif getattr(context, 'load_default_certs', None) is not None: + + elif ssl_context is None and hasattr(context, "load_default_certs"): # try to load OS default certs; works well on Windows (require Python3.4+) context.load_default_certs() + # Attempt to detect if we get the goofy behavior of the + # keyfile being encrypted and OpenSSL asking for the + # passphrase via the terminal and instead error out. + if keyfile and key_password is None and _is_key_file_encrypted(keyfile): + raise SSLError("Client private key is encrypted, password is required") + if certfile: - context.load_cert_chain(certfile, keyfile) + if key_password is None: + context.load_cert_chain(certfile, keyfile) + else: + context.load_cert_chain(certfile, keyfile, key_password) + + try: + if hasattr(context, "set_alpn_protocols"): + context.set_alpn_protocols(ALPN_PROTOCOLS) + except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols + pass # If we detect server_hostname is an IP address then the SNI # extension should not be used according to RFC3546 Section 3.1 - # We shouldn't warn the user if SNI isn't available but we would - # not be using SNI anyways due to IP address for server_hostname. - if ((server_hostname is not None and not is_ipaddress(server_hostname)) - or IS_SECURETRANSPORT): - if HAS_SNI and server_hostname is not None: - return context.wrap_socket(sock, server_hostname=server_hostname) - + use_sni_hostname = server_hostname and not is_ipaddress(server_hostname) + # SecureTransport uses server_hostname in certificate verification. + send_sni = (use_sni_hostname and HAS_SNI) or ( + IS_SECURETRANSPORT and server_hostname + ) + # Do not warn the user if server_hostname is an invalid SNI hostname. + if not HAS_SNI and use_sni_hostname: warnings.warn( - 'An HTTPS request has been made, but the SNI (Server Name ' - 'Indication) extension to TLS is not available on this platform. ' - 'This may cause the server to present an incorrect TLS ' - 'certificate, which can cause validation failures. You can upgrade to ' - 'a newer version of Python to solve this. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings', - SNIMissingWarning + "An HTTPS request has been made, but the SNI (Server Name " + "Indication) extension to TLS is not available on this platform. " + "This may cause the server to present an incorrect TLS " + "certificate, which can cause validation failures. You can upgrade to " + "a newer version of Python to solve this. For more information, see " + "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html" + "#ssl-warnings", + SNIMissingWarning, ) - return context.wrap_socket(sock) + if send_sni: + ssl_sock = _ssl_wrap_socket_impl( + sock, context, tls_in_tls, server_hostname=server_hostname + ) + else: + ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls) + return ssl_sock def is_ipaddress(hostname): - """Detects whether the hostname given is an IP address. + """Detects whether the hostname given is an IPv4 or IPv6 address. + Also detects IPv6 addresses with Zone IDs. :param str hostname: Hostname to examine. :return: True if the hostname is an IP address, False otherwise. """ - if six.PY3 and isinstance(hostname, bytes): + if not six.PY2 and isinstance(hostname, bytes): # IDN A-label bytes are ASCII compatible. - hostname = hostname.decode('ascii') + hostname = hostname.decode("ascii") + return bool(IPV4_RE.match(hostname) or BRACELESS_IPV6_ADDRZ_RE.match(hostname)) - families = [socket.AF_INET] - if hasattr(socket, 'AF_INET6'): - families.append(socket.AF_INET6) - for af in families: - try: - inet_pton(af, hostname) - except (socket.error, ValueError, OSError): - pass - else: - return True +def _is_key_file_encrypted(key_file): + """Detects if a key file is encrypted or not.""" + with open(key_file, "r") as f: + for line in f: + # Look for Proc-Type: 4,ENCRYPTED + if "ENCRYPTED" in line: + return True + return False + + +def _ssl_wrap_socket_impl(sock, ssl_context, tls_in_tls, server_hostname=None): + if tls_in_tls: + if not SSLTransport: + # Import error, ssl is not available. + raise ProxySchemeUnsupported( + "TLS in TLS requires support for the 'ssl' module" + ) + + SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) + return SSLTransport(sock, ssl_context, server_hostname) + + if server_hostname: + return ssl_context.wrap_socket(sock, server_hostname=server_hostname) + else: + return ssl_context.wrap_socket(sock) diff --git a/libs/common/urllib3/packages/ssl_match_hostname/_implementation.py b/libs/common/urllib3/util/ssl_match_hostname.py similarity index 73% rename from libs/common/urllib3/packages/ssl_match_hostname/_implementation.py rename to libs/common/urllib3/util/ssl_match_hostname.py index d6e66c01..1dd950c4 100644 --- a/libs/common/urllib3/packages/ssl_match_hostname/_implementation.py +++ b/libs/common/urllib3/util/ssl_match_hostname.py @@ -9,13 +9,13 @@ import sys # ipaddress has been backported to 2.6+ in pypi. If it is installed on the # system, use it to handle IPAddress ServerAltnames (this was added in # python-3.5) otherwise only do DNS matching. This allows -# backports.ssl_match_hostname to continue to be used in Python 2.7. +# util.ssl_match_hostname to continue to be used in Python 2.7. try: import ipaddress except ImportError: ipaddress = None -__version__ = '3.5.0.1' +__version__ = "3.5.0.1" class CertificateError(ValueError): @@ -33,18 +33,19 @@ def _dnsname_match(dn, hostname, max_wildcards=1): # Ported from python3-syntax: # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r'.') + parts = dn.split(r".") leftmost = parts[0] remainder = parts[1:] - wildcards = leftmost.count('*') + wildcards = leftmost.count("*") if wildcards > max_wildcards: # Issue #17980: avoid denials of service by refusing more # than one wildcard per fragment. A survey of established # policy among SSL implementations showed it to be a # reasonable choice. raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) + "too many wildcards in certificate DNS name: " + repr(dn) + ) # speed up common case w/o wildcards if not wildcards: @@ -53,11 +54,11 @@ def _dnsname_match(dn, hostname, max_wildcards=1): # RFC 6125, section 6.4.3, subitem 1. # The client SHOULD NOT attempt to match a presented identifier in which # the wildcard character comprises a label other than the left-most label. - if leftmost == '*': + if leftmost == "*": # When '*' is a fragment by itself, it matches a non-empty dotless # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + pats.append("[^.]+") + elif leftmost.startswith("xn--") or hostname.startswith("xn--"): # RFC 6125, section 6.4.3, subitem 3. # The client SHOULD NOT attempt to match a presented identifier # where the wildcard character is embedded within an A-label or @@ -65,21 +66,23 @@ def _dnsname_match(dn, hostname, max_wildcards=1): pats.append(re.escape(leftmost)) else: # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) # add the remaining fragments, ignore any wildcards for frag in remainder: pats.append(re.escape(frag)) - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) return pat.match(hostname) def _to_unicode(obj): if isinstance(obj, str) and sys.version_info < (3,): - obj = unicode(obj, encoding='ascii', errors='strict') + # ignored flake8 # F821 to support python 2.7 function + obj = unicode(obj, encoding="ascii", errors="strict") # noqa: F821 return obj + def _ipaddress_match(ipname, host_ip): """Exact matching of IP addresses. @@ -101,17 +104,17 @@ def match_hostname(cert, hostname): returns nothing. """ if not cert: - raise ValueError("empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED") + raise ValueError( + "empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED" + ) try: # Divergence from upstream: ipaddress can't handle byte str host_ip = ipaddress.ip_address(_to_unicode(hostname)) - except ValueError: - # Not an IP address (common case) - host_ip = None - except UnicodeError: - # Divergence from upstream: Have to deal with ipaddress not taking + except (UnicodeError, ValueError): + # ValueError: Not an IP address (common case) + # UnicodeError: Divergence from upstream: Have to deal with ipaddress not taking # byte strings. addresses should be all ascii, so we consider it not # an ipaddress in this case host_ip = None @@ -119,38 +122,38 @@ def match_hostname(cert, hostname): # Divergence from upstream: Make ipaddress library optional if ipaddress is None: host_ip = None - else: + else: # Defensive raise dnsnames = [] - san = cert.get('subjectAltName', ()) + san = cert.get("subjectAltName", ()) for key, value in san: - if key == 'DNS': + if key == "DNS": if host_ip is None and _dnsname_match(value, hostname): return dnsnames.append(value) - elif key == 'IP Address': + elif key == "IP Address": if host_ip is not None and _ipaddress_match(value, host_ip): return dnsnames.append(value) if not dnsnames: # The subject is only checked when there is no dNSName entry # in subjectAltName - for sub in cert.get('subject', ()): + for sub in cert.get("subject", ()): for key, value in sub: # XXX according to RFC 2818, the most specific Common Name # must be used. - if key == 'commonName': + if key == "commonName": if _dnsname_match(value, hostname): return dnsnames.append(value) if len(dnsnames) > 1: - raise CertificateError("hostname %r " - "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) + raise CertificateError( + "hostname %r " + "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) + ) elif len(dnsnames) == 1: - raise CertificateError("hostname %r " - "doesn't match %r" - % (hostname, dnsnames[0])) + raise CertificateError("hostname %r doesn't match %r" % (hostname, dnsnames[0])) else: - raise CertificateError("no appropriate commonName or " - "subjectAltName fields were found") + raise CertificateError( + "no appropriate commonName or subjectAltName fields were found" + ) diff --git a/libs/common/urllib3/util/ssltransport.py b/libs/common/urllib3/util/ssltransport.py new file mode 100644 index 00000000..4a7105d1 --- /dev/null +++ b/libs/common/urllib3/util/ssltransport.py @@ -0,0 +1,221 @@ +import io +import socket +import ssl + +from ..exceptions import ProxySchemeUnsupported +from ..packages import six + +SSL_BLOCKSIZE = 16384 + + +class SSLTransport: + """ + The SSLTransport wraps an existing socket and establishes an SSL connection. + + Contrary to Python's implementation of SSLSocket, it allows you to chain + multiple TLS connections together. It's particularly useful if you need to + implement TLS within TLS. + + The class supports most of the socket API operations. + """ + + @staticmethod + def _validate_ssl_context_for_tls_in_tls(ssl_context): + """ + Raises a ProxySchemeUnsupported if the provided ssl_context can't be used + for TLS in TLS. + + The only requirement is that the ssl_context provides the 'wrap_bio' + methods. + """ + + if not hasattr(ssl_context, "wrap_bio"): + if six.PY2: + raise ProxySchemeUnsupported( + "TLS in TLS requires SSLContext.wrap_bio() which isn't " + "supported on Python 2" + ) + else: + raise ProxySchemeUnsupported( + "TLS in TLS requires SSLContext.wrap_bio() which isn't " + "available on non-native SSLContext" + ) + + def __init__( + self, socket, ssl_context, server_hostname=None, suppress_ragged_eofs=True + ): + """ + Create an SSLTransport around socket using the provided ssl_context. + """ + self.incoming = ssl.MemoryBIO() + self.outgoing = ssl.MemoryBIO() + + self.suppress_ragged_eofs = suppress_ragged_eofs + self.socket = socket + + self.sslobj = ssl_context.wrap_bio( + self.incoming, self.outgoing, server_hostname=server_hostname + ) + + # Perform initial handshake. + self._ssl_io_loop(self.sslobj.do_handshake) + + def __enter__(self): + return self + + def __exit__(self, *_): + self.close() + + def fileno(self): + return self.socket.fileno() + + def read(self, len=1024, buffer=None): + return self._wrap_ssl_read(len, buffer) + + def recv(self, len=1024, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv") + return self._wrap_ssl_read(len) + + def recv_into(self, buffer, nbytes=None, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv_into") + if buffer and (nbytes is None): + nbytes = len(buffer) + elif nbytes is None: + nbytes = 1024 + return self.read(nbytes, buffer) + + def sendall(self, data, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to sendall") + count = 0 + with memoryview(data) as view, view.cast("B") as byte_view: + amount = len(byte_view) + while count < amount: + v = self.send(byte_view[count:]) + count += v + + def send(self, data, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to send") + response = self._ssl_io_loop(self.sslobj.write, data) + return response + + def makefile( + self, mode="r", buffering=None, encoding=None, errors=None, newline=None + ): + """ + Python's httpclient uses makefile and buffered io when reading HTTP + messages and we need to support it. + + This is unfortunately a copy and paste of socket.py makefile with small + changes to point to the socket directly. + """ + if not set(mode) <= {"r", "w", "b"}: + raise ValueError("invalid mode %r (only r, w, b allowed)" % (mode,)) + + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = socket.SocketIO(self, rawmode) + self.socket._io_refs += 1 + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode + return text + + def unwrap(self): + self._ssl_io_loop(self.sslobj.unwrap) + + def close(self): + self.socket.close() + + def getpeercert(self, binary_form=False): + return self.sslobj.getpeercert(binary_form) + + def version(self): + return self.sslobj.version() + + def cipher(self): + return self.sslobj.cipher() + + def selected_alpn_protocol(self): + return self.sslobj.selected_alpn_protocol() + + def selected_npn_protocol(self): + return self.sslobj.selected_npn_protocol() + + def shared_ciphers(self): + return self.sslobj.shared_ciphers() + + def compression(self): + return self.sslobj.compression() + + def settimeout(self, value): + self.socket.settimeout(value) + + def gettimeout(self): + return self.socket.gettimeout() + + def _decref_socketios(self): + self.socket._decref_socketios() + + def _wrap_ssl_read(self, len, buffer=None): + try: + return self._ssl_io_loop(self.sslobj.read, len, buffer) + except ssl.SSLError as e: + if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: + return 0 # eof, return 0. + else: + raise + + def _ssl_io_loop(self, func, *args): + """Performs an I/O loop between incoming/outgoing and the socket.""" + should_loop = True + ret = None + + while should_loop: + errno = None + try: + ret = func(*args) + except ssl.SSLError as e: + if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): + # WANT_READ, and WANT_WRITE are expected, others are not. + raise e + errno = e.errno + + buf = self.outgoing.read() + self.socket.sendall(buf) + + if errno is None: + should_loop = False + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = self.socket.recv(SSL_BLOCKSIZE) + if buf: + self.incoming.write(buf) + else: + self.incoming.write_eof() + return ret diff --git a/libs/common/urllib3/util/timeout.py b/libs/common/urllib3/util/timeout.py index cec817e6..ff69593b 100644 --- a/libs/common/urllib3/util/timeout.py +++ b/libs/common/urllib3/util/timeout.py @@ -1,8 +1,10 @@ from __future__ import absolute_import + +import time + # The default socket timeout, used by httplib to indicate that no timeout was # specified by the user from socket import _GLOBAL_DEFAULT_TIMEOUT -import time from ..exceptions import TimeoutStateError @@ -16,22 +18,28 @@ current_time = getattr(time, "monotonic", time.time) class Timeout(object): - """ Timeout configuration. + """Timeout configuration. - Timeouts can be defined as a default for a pool:: + Timeouts can be defined as a default for a pool: - timeout = Timeout(connect=2.0, read=7.0) - http = PoolManager(timeout=timeout) - response = http.request('GET', 'http://example.com/') + .. code-block:: python - Or per-request (which overrides the default for the pool):: + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') - response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + Or per-request (which overrides the default for the pool): - Timeouts can be disabled by setting all the parameters to ``None``:: + .. code-block:: python - no_timeout = Timeout(connect=None, read=None) - response = http.request('GET', 'http://example.com/, timeout=no_timeout) + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``: + + .. code-block:: python + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) :param total: @@ -42,26 +50,27 @@ class Timeout(object): Defaults to None. - :type total: integer, float, or None + :type total: int, float, or None :param connect: - The maximum amount of time to wait for a connection attempt to a server - to succeed. Omitting the parameter will default the connect timeout to - the system default, probably `the global default timeout in socket.py + The maximum amount of time (in seconds) to wait for a connection + attempt to a server to succeed. Omitting the parameter will default the + connect timeout to the system default, probably `the global default + timeout in socket.py `_. None will set an infinite timeout for connection attempts. - :type connect: integer, float, or None + :type connect: int, float, or None :param read: - The maximum amount of time to wait between consecutive - read operations for a response from the server. Omitting - the parameter will default the read timeout to the system - default, probably `the global default timeout in socket.py + The maximum amount of time (in seconds) to wait between consecutive + read operations for a response from the server. Omitting the parameter + will default the read timeout to the system default, probably `the + global default timeout in socket.py `_. None will set an infinite timeout. - :type read: integer, float, or None + :type read: int, float, or None .. note:: @@ -91,18 +100,25 @@ class Timeout(object): DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT def __init__(self, total=None, connect=_Default, read=_Default): - self._connect = self._validate_timeout(connect, 'connect') - self._read = self._validate_timeout(read, 'read') - self.total = self._validate_timeout(total, 'total') + self._connect = self._validate_timeout(connect, "connect") + self._read = self._validate_timeout(read, "read") + self.total = self._validate_timeout(total, "total") self._start_connect = None - def __str__(self): - return '%s(connect=%r, read=%r, total=%r)' % ( - type(self).__name__, self._connect, self._read, self.total) + def __repr__(self): + return "%s(connect=%r, read=%r, total=%r)" % ( + type(self).__name__, + self._connect, + self._read, + self.total, + ) + + # __str__ provided for backwards compatibility + __str__ = __repr__ @classmethod def _validate_timeout(cls, value, name): - """ Check that a timeout attribute is valid. + """Check that a timeout attribute is valid. :param value: The timeout value to validate :param name: The name of the timeout attribute to validate. This is @@ -118,28 +134,37 @@ class Timeout(object): return value if isinstance(value, bool): - raise ValueError("Timeout cannot be a boolean value. It must " - "be an int, float or None.") + raise ValueError( + "Timeout cannot be a boolean value. It must " + "be an int, float or None." + ) try: float(value) except (TypeError, ValueError): - raise ValueError("Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value)) + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) try: if value <= 0: - raise ValueError("Attempted to set %s timeout to %s, but the " - "timeout cannot be set to a value less " - "than or equal to 0." % (name, value)) - except TypeError: # Python 3 - raise ValueError("Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value)) + raise ValueError( + "Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value) + ) + except TypeError: + # Python 3 + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) return value @classmethod def from_float(cls, timeout): - """ Create a new Timeout from a legacy timeout value. + """Create a new Timeout from a legacy timeout value. The timeout value used by httplib.py sets the same timeout on the connect(), and recv() socket requests. This creates a :class:`Timeout` @@ -154,7 +179,7 @@ class Timeout(object): return Timeout(read=timeout, connect=timeout) def clone(self): - """ Create a copy of the timeout object + """Create a copy of the timeout object Timeout properties are stored per-pool but each request needs a fresh Timeout object to ensure each one has its own start/stop configured. @@ -165,11 +190,10 @@ class Timeout(object): # We can't use copy.deepcopy because that will also create a new object # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to # detect the user default. - return Timeout(connect=self._connect, read=self._read, - total=self.total) + return Timeout(connect=self._connect, read=self._read, total=self.total) def start_connect(self): - """ Start the timeout clock, used during a connect() attempt + """Start the timeout clock, used during a connect() attempt :raises urllib3.exceptions.TimeoutStateError: if you attempt to start a timer that has been started already. @@ -180,21 +204,22 @@ class Timeout(object): return self._start_connect def get_connect_duration(self): - """ Gets the time elapsed since the call to :meth:`start_connect`. + """Gets the time elapsed since the call to :meth:`start_connect`. - :return: Elapsed time. + :return: Elapsed time in seconds. :rtype: float :raises urllib3.exceptions.TimeoutStateError: if you attempt to get duration for a timer that hasn't been started. """ if self._start_connect is None: - raise TimeoutStateError("Can't get connect duration for timer " - "that has not started.") + raise TimeoutStateError( + "Can't get connect duration for timer that has not started." + ) return current_time() - self._start_connect @property def connect_timeout(self): - """ Get the value to use when setting a connection timeout. + """Get the value to use when setting a connection timeout. This will be a positive float or integer, the value None (never timeout), or the default system timeout. @@ -212,7 +237,7 @@ class Timeout(object): @property def read_timeout(self): - """ Get the value for the read timeout. + """Get the value for the read timeout. This assumes some time has elapsed in the connection timeout and computes the read timeout appropriately. @@ -227,15 +252,16 @@ class Timeout(object): :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` has not yet been called on this object. """ - if (self.total is not None and - self.total is not self.DEFAULT_TIMEOUT and - self._read is not None and - self._read is not self.DEFAULT_TIMEOUT): + if ( + self.total is not None + and self.total is not self.DEFAULT_TIMEOUT + and self._read is not None + and self._read is not self.DEFAULT_TIMEOUT + ): # In case the connect timeout has not yet been established. if self._start_connect is None: return self._read - return max(0, min(self.total - self.get_connect_duration(), - self._read)) + return max(0, min(self.total - self.get_connect_duration(), self._read)) elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: return max(0, self.total - self.get_connect_duration()) else: diff --git a/libs/common/urllib3/util/url.py b/libs/common/urllib3/util/url.py index 6b6f9968..94f1b8d4 100644 --- a/libs/common/urllib3/util/url.py +++ b/libs/common/urllib3/util/url.py @@ -1,34 +1,110 @@ from __future__ import absolute_import + +import re from collections import namedtuple from ..exceptions import LocationParseError +from ..packages import six - -url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] +url_attrs = ["scheme", "auth", "host", "port", "path", "query", "fragment"] # We only want to normalize urls with an HTTP(S) scheme. # urllib3 infers URLs without a scheme (None) to be http. -NORMALIZABLE_SCHEMES = ('http', 'https', None) +NORMALIZABLE_SCHEMES = ("http", "https", None) + +# Almost all of these patterns were derived from the +# 'rfc3986' module: https://github.com/python-hyper/rfc3986 +PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") +SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") +URI_RE = re.compile( + r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" + r"(?://([^\\/?#]*))?" + r"([^?#]*)" + r"(?:\?([^#]*))?" + r"(?:#(.*))?$", + re.UNICODE | re.DOTALL, +) + +IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" +HEX_PAT = "[0-9A-Fa-f]{1,4}" +LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=HEX_PAT, ipv4=IPV4_PAT) +_subs = {"hex": HEX_PAT, "ls32": LS32_PAT} +_variations = [ + # 6( h16 ":" ) ls32 + "(?:%(hex)s:){6}%(ls32)s", + # "::" 5( h16 ":" ) ls32 + "::(?:%(hex)s:){5}%(ls32)s", + # [ h16 ] "::" 4( h16 ":" ) ls32 + "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", + # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", + # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", + # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", + # [ *4( h16 ":" ) h16 ] "::" ls32 + "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", + # [ *5( h16 ":" ) h16 ] "::" h16 + "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", + # [ *6( h16 ":" ) h16 ] "::" + "(?:(?:%(hex)s:){0,6}%(hex)s)?::", +] + +UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._!\-~" +IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" +ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" +IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]" +REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" +TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") + +IPV4_RE = re.compile("^" + IPV4_PAT + "$") +IPV6_RE = re.compile("^" + IPV6_PAT + "$") +IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$") +BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") +ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") + +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*([0-9]{0,5}))?$") % ( + REG_NAME_PAT, + IPV4_PAT, + IPV6_ADDRZ_PAT, +) +_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) + +UNRESERVED_CHARS = set( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" +) +SUB_DELIM_CHARS = set("!$&'()*+,;=") +USERINFO_CHARS = UNRESERVED_CHARS | SUB_DELIM_CHARS | {":"} +PATH_CHARS = USERINFO_CHARS | {"@", "/"} +QUERY_CHARS = FRAGMENT_CHARS = PATH_CHARS | {"?"} -class Url(namedtuple('Url', url_attrs)): +class Url(namedtuple("Url", url_attrs)): """ - Datastructure for representing an HTTP URL. Used as a return value for + Data structure for representing an HTTP URL. Used as a return value for :func:`parse_url`. Both the scheme and host are normalized as they are both case-insensitive according to RFC 3986. """ + __slots__ = () - def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, - query=None, fragment=None): - if path and not path.startswith('/'): - path = '/' + path - if scheme: + def __new__( + cls, + scheme=None, + auth=None, + host=None, + port=None, + path=None, + query=None, + fragment=None, + ): + if path and not path.startswith("/"): + path = "/" + path + if scheme is not None: scheme = scheme.lower() - if host and scheme in NORMALIZABLE_SCHEMES: - host = host.lower() - return super(Url, cls).__new__(cls, scheme, auth, host, port, path, - query, fragment) + return super(Url, cls).__new__( + cls, scheme, auth, host, port, path, query, fragment + ) @property def hostname(self): @@ -38,10 +114,10 @@ class Url(namedtuple('Url', url_attrs)): @property def request_uri(self): """Absolute path including the query string.""" - uri = self.path or '/' + uri = self.path or "/" if self.query is not None: - uri += '?' + self.query + uri += "?" + self.query return uri @@ -49,7 +125,7 @@ class Url(namedtuple('Url', url_attrs)): def netloc(self): """Network location including host and port""" if self.port: - return '%s:%d' % (self.host, self.port) + return "%s:%d" % (self.host, self.port) return self.host @property @@ -72,23 +148,23 @@ class Url(namedtuple('Url', url_attrs)): 'http://username:password@host.com:80/path?query#fragment' """ scheme, auth, host, port, path, query, fragment = self - url = '' + url = u"" # We use "is not None" we want things to happen with empty strings (or 0 port) if scheme is not None: - url += scheme + '://' + url += scheme + u"://" if auth is not None: - url += auth + '@' + url += auth + u"@" if host is not None: url += host if port is not None: - url += ':' + str(port) + url += u":" + str(port) if path is not None: url += path if query is not None: - url += '?' + query + url += u"?" + query if fragment is not None: - url += '#' + fragment + url += u"#" + fragment return url @@ -98,6 +174,8 @@ class Url(namedtuple('Url', url_attrs)): def split_first(s, delims): """ + .. deprecated:: 1.25 + Given a string and an iterable of delimiters, split on the first found delimiter. Return two split parts and the matched delimiter. @@ -124,15 +202,144 @@ def split_first(s, delims): min_delim = d if min_idx is None or min_idx < 0: - return s, '', None + return s, "", None - return s[:min_idx], s[min_idx + 1:], min_delim + return s[:min_idx], s[min_idx + 1 :], min_delim + + +def _encode_invalid_chars(component, allowed_chars, encoding="utf-8"): + """Percent-encodes a URI component without reapplying + onto an already percent-encoded component. + """ + if component is None: + return component + + component = six.ensure_text(component) + + # Normalize existing percent-encoded bytes. + # Try to see if the component we're encoding is already percent-encoded + # so we can skip all '%' characters but still encode all others. + component, percent_encodings = PERCENT_RE.subn( + lambda match: match.group(0).upper(), component + ) + + uri_bytes = component.encode("utf-8", "surrogatepass") + is_percent_encoded = percent_encodings == uri_bytes.count(b"%") + encoded_component = bytearray() + + for i in range(0, len(uri_bytes)): + # Will return a single character bytestring on both Python 2 & 3 + byte = uri_bytes[i : i + 1] + byte_ord = ord(byte) + if (is_percent_encoded and byte == b"%") or ( + byte_ord < 128 and byte.decode() in allowed_chars + ): + encoded_component += byte + continue + encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) + + return encoded_component.decode(encoding) + + +def _remove_path_dot_segments(path): + # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code + segments = path.split("/") # Turn the path into a list of segments + output = [] # Initialize the variable to use to store output + + for segment in segments: + # '.' is the current directory, so ignore it, it is superfluous + if segment == ".": + continue + # Anything other than '..', should be appended to the output + elif segment != "..": + output.append(segment) + # In this case segment == '..', if we can, we should pop the last + # element + elif output: + output.pop() + + # If the path starts with '/' and the output is empty or the first string + # is non-empty + if path.startswith("/") and (not output or output[0]): + output.insert(0, "") + + # If the path starts with '/.' or '/..' ensure we add one more empty + # string to add a trailing '/' + if path.endswith(("/.", "/..")): + output.append("") + + return "/".join(output) + + +def _normalize_host(host, scheme): + if host: + if isinstance(host, six.binary_type): + host = six.ensure_str(host) + + if scheme in NORMALIZABLE_SCHEMES: + is_ipv6 = IPV6_ADDRZ_RE.match(host) + if is_ipv6: + # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as + # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID + # separator as necessary to return a valid RFC 4007 scoped IP. + match = ZONE_ID_RE.search(host) + if match: + start, end = match.span(1) + zone_id = host[start:end] + + if zone_id.startswith("%25") and zone_id != "%25": + zone_id = zone_id[3:] + else: + zone_id = zone_id[1:] + zone_id = "%" + _encode_invalid_chars(zone_id, UNRESERVED_CHARS) + return host[:start].lower() + zone_id + host[end:] + else: + return host.lower() + elif not IPV4_RE.match(host): + return six.ensure_str( + b".".join([_idna_encode(label) for label in host.split(".")]) + ) + return host + + +def _idna_encode(name): + if name and any([ord(x) > 128 for x in name]): + try: + import idna + except ImportError: + six.raise_from( + LocationParseError("Unable to parse URL without the 'idna' module"), + None, + ) + try: + return idna.encode(name.lower(), strict=True, std3_rules=True) + except idna.IDNAError: + six.raise_from( + LocationParseError(u"Name '%s' is not a valid IDNA label" % name), None + ) + return name.lower().encode("ascii") + + +def _encode_target(target): + """Percent-encodes a request target so that there are no invalid characters""" + path, query = TARGET_RE.match(target).groups() + target = _encode_invalid_chars(path, PATH_CHARS) + query = _encode_invalid_chars(query, QUERY_CHARS) + if query is not None: + target += "?" + query + return target def parse_url(url): """ Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is performed to parse incomplete urls. Fields not provided will be None. + This parser is RFC 3986 and RFC 6874 compliant. + + The parser logic and helper functions are based heavily on + work done in the ``rfc3986`` module. + + :param str url: URL to parse into a :class:`.Url` namedtuple. Partly backwards-compatible with :mod:`urlparse`. @@ -145,81 +352,79 @@ def parse_url(url): >>> parse_url('/foo?bar') Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) """ - - # While this code has overlap with stdlib's urlparse, it is much - # simplified for our needs and less annoying. - # Additionally, this implementations does silly things to be optimal - # on CPython. - if not url: # Empty return Url() - scheme = None - auth = None - host = None - port = None - path = None - fragment = None - query = None + source_url = url + if not SCHEME_RE.search(url): + url = "//" + url - # Scheme - if '://' in url: - scheme, url = url.split('://', 1) + try: + scheme, authority, path, query, fragment = URI_RE.match(url).groups() + normalize_uri = scheme is None or scheme.lower() in NORMALIZABLE_SCHEMES - # Find the earliest Authority Terminator - # (http://tools.ietf.org/html/rfc3986#section-3.2) - url, path_, delim = split_first(url, ['/', '?', '#']) + if scheme: + scheme = scheme.lower() - if delim: - # Reassemble the path - path = delim + path_ - - # Auth - if '@' in url: - # Last '@' denotes end of auth part - auth, url = url.rsplit('@', 1) - - # IPv6 - if url and url[0] == '[': - host, url = url.split(']', 1) - host += ']' - - # Port - if ':' in url: - _host, port = url.split(':', 1) - - if not host: - host = _host - - if port: - # If given, ports must be integers. No whitespace, no plus or - # minus prefixes, no non-integer digits such as ^2 (superscript). - if not port.isdigit(): - raise LocationParseError(url) - try: - port = int(port) - except ValueError: - raise LocationParseError(url) + if authority: + auth, _, host_port = authority.rpartition("@") + auth = auth or None + host, port = _HOST_PORT_RE.match(host_port).groups() + if auth and normalize_uri: + auth = _encode_invalid_chars(auth, USERINFO_CHARS) + if port == "": + port = None else: - # Blank ports are cool, too. (rfc3986#section-3.2.3) - port = None + auth, host, port = None, None, None - elif not host and url: - host = url + if port is not None: + port = int(port) + if not (0 <= port <= 65535): + raise LocationParseError(url) + host = _normalize_host(host, scheme) + + if normalize_uri and path: + path = _remove_path_dot_segments(path) + path = _encode_invalid_chars(path, PATH_CHARS) + if normalize_uri and query: + query = _encode_invalid_chars(query, QUERY_CHARS) + if normalize_uri and fragment: + fragment = _encode_invalid_chars(fragment, FRAGMENT_CHARS) + + except (ValueError, AttributeError): + return six.raise_from(LocationParseError(source_url), None) + + # For the sake of backwards compatibility we put empty + # string values for path if there are any defined values + # beyond the path in the URL. + # TODO: Remove this when we break backwards compatibility. if not path: - return Url(scheme, auth, host, port, path, query, fragment) + if query is not None or fragment is not None: + path = "" + else: + path = None - # Fragment - if '#' in path: - path, fragment = path.split('#', 1) + # Ensure that each part of the URL is a `str` for + # backwards compatibility. + if isinstance(url, six.text_type): + ensure_func = six.ensure_text + else: + ensure_func = six.ensure_str - # Query - if '?' in path: - path, query = path.split('?', 1) + def ensure_type(x): + return x if x is None else ensure_func(x) - return Url(scheme, auth, host, port, path, query, fragment) + return Url( + scheme=ensure_type(scheme), + auth=ensure_type(auth), + host=ensure_type(host), + port=port, + path=ensure_type(path), + query=ensure_type(query), + fragment=ensure_type(fragment), + ) def get_host(url): @@ -227,4 +432,4 @@ def get_host(url): Deprecated. Use :func:`parse_url` instead. """ p = parse_url(url) - return p.scheme or 'http', p.hostname, p.port + return p.scheme or "http", p.hostname, p.port diff --git a/libs/common/urllib3/util/wait.py b/libs/common/urllib3/util/wait.py index 4db71baf..21b4590b 100644 --- a/libs/common/urllib3/util/wait.py +++ b/libs/common/urllib3/util/wait.py @@ -1,7 +1,8 @@ import errno -from functools import partial import select import sys +from functools import partial + try: from time import monotonic except ImportError: @@ -40,6 +41,7 @@ if sys.version_info >= (3, 5): # Modern Python, that retries syscalls by default def _retry_on_intr(fn, timeout): return fn(timeout) + else: # Old and broken Pythons. def _retry_on_intr(fn, timeout): @@ -137,14 +139,14 @@ def wait_for_socket(*args, **kwargs): def wait_for_read(sock, timeout=None): - """ Waits for reading to be available on a given socket. + """Waits for reading to be available on a given socket. Returns True if the socket is readable, or False if the timeout expired. """ return wait_for_socket(sock, read=True, timeout=timeout) def wait_for_write(sock, timeout=None): - """ Waits for writing to be available on a given socket. + """Waits for writing to be available on a given socket. Returns True if the socket is readable, or False if the timeout expired. """ return wait_for_socket(sock, write=True, timeout=timeout) From ebc9718117359a6123d3f681fe108f0c4b2dab1d Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 19:33:12 -0500 Subject: [PATCH 32/47] Update vendored requests-oauthlib to 1.3.1 --- libs/common/bin/chardetect.exe | Bin 108383 -> 108383 bytes libs/common/requests_oauthlib/__init__.py | 2 +- .../compliance_fixes/__init__.py | 2 +- .../compliance_fixes/ebay.py | 23 ++++++++++++++++++ .../compliance_fixes/linkedin.py | 21 ---------------- .../requests_oauthlib/oauth1_session.py | 2 +- .../requests_oauthlib/oauth2_session.py | 6 +++++ 7 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 libs/common/requests_oauthlib/compliance_fixes/ebay.py delete mode 100644 libs/common/requests_oauthlib/compliance_fixes/linkedin.py diff --git a/libs/common/bin/chardetect.exe b/libs/common/bin/chardetect.exe index 193b49855af679b788efea9ecd2fc5e4900ccccb..f93a4a82c2b5e499858de8dd7a0422f0eb9ce115 100644 GIT binary patch delta 27 hcmcbAj_v+AwuUW?>dTn^&YrHhjPWywmRru~2mrit43z)? delta 27 hcmcbAj_v+AwuUW?>dTnE&YZ5fjPWywmRru~2mrg941oXu diff --git a/libs/common/requests_oauthlib/__init__.py b/libs/common/requests_oauthlib/__init__.py index a4e03a4e..0d3e49f9 100644 --- a/libs/common/requests_oauthlib/__init__.py +++ b/libs/common/requests_oauthlib/__init__.py @@ -5,7 +5,7 @@ from .oauth1_session import OAuth1Session from .oauth2_auth import OAuth2 from .oauth2_session import OAuth2Session, TokenUpdated -__version__ = "1.3.0" +__version__ = "1.3.1" import requests diff --git a/libs/common/requests_oauthlib/compliance_fixes/__init__.py b/libs/common/requests_oauthlib/compliance_fixes/__init__.py index 02fa5120..0e8e3ac8 100644 --- a/libs/common/requests_oauthlib/compliance_fixes/__init__.py +++ b/libs/common/requests_oauthlib/compliance_fixes/__init__.py @@ -2,9 +2,9 @@ from __future__ import absolute_import from .facebook import facebook_compliance_fix from .fitbit import fitbit_compliance_fix -from .linkedin import linkedin_compliance_fix from .slack import slack_compliance_fix from .instagram import instagram_compliance_fix from .mailchimp import mailchimp_compliance_fix from .weibo import weibo_compliance_fix from .plentymarkets import plentymarkets_compliance_fix +from .ebay import ebay_compliance_fix diff --git a/libs/common/requests_oauthlib/compliance_fixes/ebay.py b/libs/common/requests_oauthlib/compliance_fixes/ebay.py new file mode 100644 index 00000000..4aa423b3 --- /dev/null +++ b/libs/common/requests_oauthlib/compliance_fixes/ebay.py @@ -0,0 +1,23 @@ +import json +from oauthlib.common import to_unicode + + +def ebay_compliance_fix(session): + def _compliance_fix(response): + token = json.loads(response.text) + + # eBay responds with non-compliant token types. + # https://developer.ebay.com/api-docs/static/oauth-client-credentials-grant.html + # https://developer.ebay.com/api-docs/static/oauth-auth-code-grant-request.html + # Modify these to be "Bearer". + if token.get("token_type") in ["Application Access Token", "User Access Token"]: + token["token_type"] = "Bearer" + fixed_token = json.dumps(token) + response._content = to_unicode(fixed_token).encode("utf-8") + + return response + + session.register_compliance_hook("access_token_response", _compliance_fix) + session.register_compliance_hook("refresh_token_response", _compliance_fix) + + return session diff --git a/libs/common/requests_oauthlib/compliance_fixes/linkedin.py b/libs/common/requests_oauthlib/compliance_fixes/linkedin.py deleted file mode 100644 index cd5b4ace..00000000 --- a/libs/common/requests_oauthlib/compliance_fixes/linkedin.py +++ /dev/null @@ -1,21 +0,0 @@ -from json import loads, dumps - -from oauthlib.common import add_params_to_uri, to_unicode - - -def linkedin_compliance_fix(session): - def _missing_token_type(r): - token = loads(r.text) - token["token_type"] = "Bearer" - r._content = to_unicode(dumps(token)).encode("UTF-8") - return r - - def _non_compliant_param_name(url, headers, data): - token = [("oauth2_access_token", session.access_token)] - url = add_params_to_uri(url, token) - return url, headers, data - - session._client.default_token_placement = "query" - session.register_compliance_hook("access_token_response", _missing_token_type) - session.register_compliance_hook("protected_request", _non_compliant_param_name) - return session diff --git a/libs/common/requests_oauthlib/oauth1_session.py b/libs/common/requests_oauthlib/oauth1_session.py index aa17f28f..88f2853c 100644 --- a/libs/common/requests_oauthlib/oauth1_session.py +++ b/libs/common/requests_oauthlib/oauth1_session.py @@ -268,7 +268,7 @@ class OAuth1Session(requests.Session): :param url: The request token endpoint URL. :param realm: A list of realms to request access to. :param \*\*request_kwargs: Optional arguments passed to ''post'' - function in ''requests.Session'' + function in ''requests.Session'' :returns: The response in dict format. Note that a previously set callback_uri will be reset for your diff --git a/libs/common/requests_oauthlib/oauth2_session.py b/libs/common/requests_oauthlib/oauth2_session.py index eea4ac6f..db446808 100644 --- a/libs/common/requests_oauthlib/oauth2_session.py +++ b/libs/common/requests_oauthlib/oauth2_session.py @@ -189,6 +189,7 @@ class OAuth2Session(requests.Session): proxies=None, include_client_id=None, client_secret=None, + cert=None, **kwargs ): """Generic method for fetching an access token from the token endpoint. @@ -229,6 +230,10 @@ class OAuth2Session(requests.Session): `auth` tuple. If the value is `None`, it will be omitted from the request, however if the value is an empty string, an empty string will be sent. + :param cert: Client certificate to send for OAuth 2.0 Mutual-TLS Client + Authentication (draft-ietf-oauth-mtls). Can either be the + path of a file containing the private key and certificate or + a tuple of two filenames for certificate and key. :param kwargs: Extra parameters to include in the token request. :return: A token dict """ @@ -341,6 +346,7 @@ class OAuth2Session(requests.Session): auth=auth, verify=verify, proxies=proxies, + cert=cert, **request_kwargs ) From 2226a74ef84293442674ae458f9e867902bc5094 Mon Sep 17 00:00:00 2001 From: Labrys of Knossos Date: Mon, 28 Nov 2022 19:44:46 -0500 Subject: [PATCH 33/47] Update vendored guessit to 3.1.1 Updates python-dateutil to 2.8.2 Updates rebulk to 2.0.1 --- .../bin/{chardetect.exe => guessit.exe} | Bin 108383 -> 108377 bytes libs/common/dateutil/_version.py | 3 +- libs/common/dateutil/easter.py | 16 +- libs/common/dateutil/parser/__init__.py | 3 +- libs/common/dateutil/parser/_parser.py | 163 +++--- libs/common/dateutil/parser/isoparser.py | 56 ++- libs/common/dateutil/relativedelta.py | 33 +- libs/common/dateutil/rrule.py | 207 +++++--- libs/common/dateutil/tz/__init__.py | 5 - libs/common/dateutil/tz/_common.py | 22 +- libs/common/dateutil/tz/_factories.py | 35 +- libs/common/dateutil/tz/tz.py | 168 +++++-- libs/common/dateutil/tz/win.py | 45 +- libs/common/dateutil/utils.py | 4 +- .../zoneinfo/dateutil-zoneinfo.tar.gz | Bin 154226 -> 174394 bytes libs/common/dateutil/zoneinfo/rebuild.py | 34 +- libs/common/guessit/__main__.py | 2 +- libs/common/guessit/__version__.py | 2 +- libs/common/guessit/api.py | 31 ++ libs/common/guessit/backports.py | 2 +- libs/common/guessit/config/options.json | 262 +++++++++- libs/common/guessit/options.py | 6 +- .../common/guessit/rules/common/formatters.py | 2 +- .../common/guessit/rules/common/validators.py | 25 +- libs/common/guessit/rules/match_processors.py | 20 + libs/common/guessit/rules/processors.py | 4 +- .../guessit/rules/properties/audio_codec.py | 29 +- .../guessit/rules/properties/bit_rate.py | 4 +- libs/common/guessit/rules/properties/bonus.py | 3 +- .../guessit/rules/properties/container.py | 3 +- .../guessit/rules/properties/episode_title.py | 12 +- .../guessit/rules/properties/episodes.py | 315 +++++++----- .../guessit/rules/properties/language.py | 11 +- libs/common/guessit/rules/properties/other.py | 39 +- libs/common/guessit/rules/properties/part.py | 4 +- .../guessit/rules/properties/release_group.py | 36 +- .../guessit/rules/properties/screen_size.py | 4 +- .../common/guessit/rules/properties/source.py | 82 ++- .../rules/properties/streaming_service.py | 136 +---- libs/common/guessit/rules/properties/title.py | 31 +- .../guessit/rules/properties/video_codec.py | 9 +- .../guessit/rules/properties/website.py | 6 +- .../test/enable_disable_properties.yml | 8 +- libs/common/guessit/test/episodes.yml | 180 ++++++- libs/common/guessit/test/movies.yml | 21 +- .../guessit/test/rules/common_words.yml | 467 ++++++++++++++++++ libs/common/guessit/test/rules/country.yml | 4 +- libs/common/guessit/test/rules/other.yml | 4 +- libs/common/guessit/test/suggested.json | 21 + libs/common/guessit/test/test_api.py | 26 +- libs/common/guessit/test/test_yml.py | 113 +++-- libs/common/guessit/test/various.yml | 251 ++++++++++ libs/common/guessit/yamlutils.py | 8 +- libs/common/rebulk/__version__.py | 2 +- libs/common/rebulk/builder.py | 217 ++++++++ libs/common/rebulk/chain.py | 347 +++++-------- libs/common/rebulk/formatters.py | 10 + libs/common/rebulk/introspector.py | 5 +- libs/common/rebulk/loose.py | 30 +- libs/common/rebulk/match.py | 18 + libs/common/rebulk/pattern.py | 356 +++++++------ libs/common/rebulk/rebulk.py | 183 +------ libs/common/rebulk/test/test_chain.py | 106 ++-- libs/common/rebulk/test/test_debug.py | 36 +- libs/common/rebulk/test/test_match.py | 3 + libs/common/rebulk/validators.py | 11 + 66 files changed, 2995 insertions(+), 1306 deletions(-) rename libs/common/bin/{chardetect.exe => guessit.exe} (99%) create mode 100644 libs/common/guessit/rules/match_processors.py create mode 100644 libs/common/guessit/test/rules/common_words.yml create mode 100644 libs/common/guessit/test/suggested.json create mode 100644 libs/common/rebulk/builder.py diff --git a/libs/common/bin/chardetect.exe b/libs/common/bin/guessit.exe similarity index 99% rename from libs/common/bin/chardetect.exe rename to libs/common/bin/guessit.exe index f93a4a82c2b5e499858de8dd7a0422f0eb9ce115..550c3cb32d3825f6a7a3b44b729a50ac7d5919f5 100644 GIT binary patch delta 78 zcmcbAj_u|-wuUW?>dSab=GKH-H;7z2!oa|AWV*>RMi+tf($wPO%o4r$_}s+Iy!iO( Veajg8&=hJeXEb1Y3)I8F007wm9GL(B delta 84 zcmcb4j_v+AwuUW?>dScl&aMe9@pasEih+UQ)O3?&j4ooy8Hq(HsU>>JIhlG;c4~6T W^p0hWeQ3&+mopl$eFo}eU;qHk#2^p= diff --git a/libs/common/dateutil/_version.py b/libs/common/dateutil/_version.py index d3ce8561..b723056a 100644 --- a/libs/common/dateutil/_version.py +++ b/libs/common/dateutil/_version.py @@ -1,4 +1,5 @@ # coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control -version = '2.7.5' +version = '2.8.2' +version_tuple = (2, 8, 2) diff --git a/libs/common/dateutil/easter.py b/libs/common/dateutil/easter.py index 53b7c789..f74d1f74 100644 --- a/libs/common/dateutil/easter.py +++ b/libs/common/dateutil/easter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -This module offers a generic easter computing method for any given year, using +This module offers a generic Easter computing method for any given year, using Western, Orthodox or Julian algorithms. """ @@ -21,15 +21,15 @@ def easter(year, method=EASTER_WESTERN): quoted in "Explanatory Supplement to the Astronomical Almanac", P. Kenneth Seidelmann, editor. - This algorithm implements three different easter + This algorithm implements three different Easter calculation methods: - 1 - Original calculation in Julian calendar, valid in - dates after 326 AD - 2 - Original method, with date converted to Gregorian - calendar, valid in years 1583 to 4099 - 3 - Revised method, in Gregorian calendar, valid in - years 1583 to 4099 as well + 1. Original calculation in Julian calendar, valid in + dates after 326 AD + 2. Original method, with date converted to Gregorian + calendar, valid in years 1583 to 4099 + 3. Revised method, in Gregorian calendar, valid in + years 1583 to 4099 as well These methods are represented by the constants: diff --git a/libs/common/dateutil/parser/__init__.py b/libs/common/dateutil/parser/__init__.py index 216762c0..d174b0e4 100644 --- a/libs/common/dateutil/parser/__init__.py +++ b/libs/common/dateutil/parser/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from ._parser import parse, parser, parserinfo +from ._parser import parse, parser, parserinfo, ParserError from ._parser import DEFAULTPARSER, DEFAULTTZPARSER from ._parser import UnknownTimezoneWarning @@ -9,6 +9,7 @@ from .isoparser import isoparser, isoparse __all__ = ['parse', 'parser', 'parserinfo', 'isoparse', 'isoparser', + 'ParserError', 'UnknownTimezoneWarning'] diff --git a/libs/common/dateutil/parser/_parser.py b/libs/common/dateutil/parser/_parser.py index 9d2bb795..37d1663b 100644 --- a/libs/common/dateutil/parser/_parser.py +++ b/libs/common/dateutil/parser/_parser.py @@ -20,11 +20,11 @@ value falls back to the end of the month. Additional resources about date/time string formats can be found below: - `A summary of the international standard date and time notation - `_ -- `W3C Date and Time Formats `_ + `_ +- `W3C Date and Time Formats `_ - `Time Formats (Planetary Rings Node) `_ - `CPAN ParseDate module - `_ + `_ - `Java SimpleDateFormat Class `_ """ @@ -40,7 +40,7 @@ from calendar import monthrange from io import StringIO import six -from six import binary_type, integer_types, text_type +from six import integer_types, text_type from decimal import Decimal @@ -49,7 +49,7 @@ from warnings import warn from .. import relativedelta from .. import tz -__all__ = ["parse", "parserinfo"] +__all__ = ["parse", "parserinfo", "ParserError"] # TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth @@ -60,14 +60,8 @@ class _timelex(object): _split_decimal = re.compile("([.,])") def __init__(self, instream): - if six.PY2: - # In Python 2, we can't duck type properly because unicode has - # a 'decode' function, and we'd be double-decoding - if isinstance(instream, (binary_type, bytearray)): - instream = instream.decode() - else: - if getattr(instream, 'decode', None) is not None: - instream = instream.decode() + if isinstance(instream, (bytes, bytearray)): + instream = instream.decode() if isinstance(instream, text_type): instream = StringIO(instream) @@ -291,7 +285,7 @@ class parserinfo(object): ("s", "second", "seconds")] AMPM = [("am", "a"), ("pm", "p")] - UTCZONE = ["UTC", "GMT", "Z"] + UTCZONE = ["UTC", "GMT", "Z", "z"] PERTAIN = ["of"] TZOFFSET = {} # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", @@ -388,7 +382,8 @@ class parserinfo(object): if res.year is not None: res.year = self.convertyear(res.year, res.century_specified) - if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': + if ((res.tzoffset == 0 and not res.tzname) or + (res.tzname == 'Z' or res.tzname == 'z')): res.tzname = "UTC" res.tzoffset = 0 elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): @@ -422,7 +417,7 @@ class _ymd(list): elif not self.has_month: return 1 <= value <= 31 elif not self.has_year: - # Be permissive, assume leapyear + # Be permissive, assume leap year month = self[self.mstridx] return 1 <= value <= monthrange(2000, month)[1] else: @@ -538,7 +533,7 @@ class _ymd(list): year, month, day = self else: # 01-Jan-01 - # Give precendence to day-first, since + # Give precedence to day-first, since # two-digit years is usually hand-written. day, month, year = self @@ -625,7 +620,7 @@ class parser(object): first element being a :class:`datetime.datetime` object, the second a tuple containing the fuzzy tokens. - :raises ValueError: + :raises ParserError: Raised for invalid or unknown string format, if the provided :class:`tzinfo` is not in a valid format, or if an invalid date would be created. @@ -645,12 +640,15 @@ class parser(object): res, skipped_tokens = self._parse(timestr, **kwargs) if res is None: - raise ValueError("Unknown string format:", timestr) + raise ParserError("Unknown string format: %s", timestr) if len(res) == 0: - raise ValueError("String does not contain a date:", timestr) + raise ParserError("String does not contain a date: %s", timestr) - ret = self._build_naive(res, default) + try: + ret = self._build_naive(res, default) + except ValueError as e: + six.raise_from(ParserError(str(e) + ": %s", timestr), e) if not ignoretz: ret = self._build_tzaware(ret, res, tzinfos) @@ -1021,7 +1019,7 @@ class parser(object): hms_idx = idx + 2 elif idx > 0 and info.hms(tokens[idx-1]) is not None: - # There is a "h", "m", or "s" preceeding this token. Since neither + # There is a "h", "m", or "s" preceding this token. Since neither # of the previous cases was hit, there is no label following this # token, so we use the previous label. # e.g. the "04" in "12h04" @@ -1060,7 +1058,8 @@ class parser(object): tzname is None and tzoffset is None and len(token) <= 5 and - all(x in string.ascii_uppercase for x in token)) + (all(x in string.ascii_uppercase for x in token) + or token in self.info.UTCZONE)) def _ampm_valid(self, hour, ampm, fuzzy): """ @@ -1100,7 +1099,7 @@ class parser(object): def _parse_min_sec(self, value): # TODO: Every usage of this function sets res.second to the return # value. Are there any cases where second will be returned as None and - # we *dont* want to set res.second = None? + # we *don't* want to set res.second = None? minute = int(value) second = None @@ -1109,14 +1108,6 @@ class parser(object): second = int(60 * sec_remainder) return (minute, second) - def _parsems(self, value): - """Parse a I[.F] seconds value into (seconds, microseconds).""" - if "." not in value: - return int(value), 0 - else: - i, f = value.split(".") - return int(i), int(f.ljust(6, "0")[:6]) - def _parse_hms(self, idx, tokens, info, hms_idx): # TODO: Is this going to admit a lot of false-positives for when we # just happen to have digits and "h", "m" or "s" characters in non-date @@ -1135,21 +1126,35 @@ class parser(object): return (new_idx, hms) - def _recombine_skipped(self, tokens, skipped_idxs): - """ - >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] - >>> skipped_idxs = [0, 1, 2, 5] - >>> _recombine_skipped(tokens, skipped_idxs) - ["foo bar", "baz"] - """ - skipped_tokens = [] - for i, idx in enumerate(sorted(skipped_idxs)): - if i > 0 and idx - 1 == skipped_idxs[i - 1]: - skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] - else: - skipped_tokens.append(tokens[idx]) + # ------------------------------------------------------------------ + # Handling for individual tokens. These are kept as methods instead + # of functions for the sake of customizability via subclassing. - return skipped_tokens + def _parsems(self, value): + """Parse a I[.F] seconds value into (seconds, microseconds).""" + if "." not in value: + return int(value), 0 + else: + i, f = value.split(".") + return int(i), int(f.ljust(6, "0")[:6]) + + def _to_decimal(self, val): + try: + decimal_value = Decimal(val) + # See GH 662, edge case, infinite value should not be converted + # via `_to_decimal` + if not decimal_value.is_finite(): + raise ValueError("Converted decimal value is infinite or NaN") + except Exception as e: + msg = "Could not convert %s to decimal" % val + six.raise_from(ValueError(msg), e) + else: + return decimal_value + + # ------------------------------------------------------------------ + # Post-Parsing construction of datetime output. These are kept as + # methods instead of functions for the sake of customizability via + # subclassing. def _build_tzinfo(self, tzinfos, tzname, tzoffset): if callable(tzinfos): @@ -1164,6 +1169,9 @@ class parser(object): tzinfo = tz.tzstr(tzdata) elif isinstance(tzdata, integer_types): tzinfo = tz.tzoffset(tzname, tzdata) + else: + raise TypeError("Offset must be tzinfo subclass, tz string, " + "or int offset.") return tzinfo def _build_tzaware(self, naive, res, tzinfos): @@ -1181,10 +1189,10 @@ class parser(object): # This is mostly relevant for winter GMT zones parsed in the UK if (aware.tzname() != res.tzname and res.tzname in self.info.UTCZONE): - aware = aware.replace(tzinfo=tz.tzutc()) + aware = aware.replace(tzinfo=tz.UTC) elif res.tzoffset == 0: - aware = naive.replace(tzinfo=tz.tzutc()) + aware = naive.replace(tzinfo=tz.UTC) elif res.tzoffset: aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) @@ -1239,17 +1247,21 @@ class parser(object): return dt - def _to_decimal(self, val): - try: - decimal_value = Decimal(val) - # See GH 662, edge case, infinite value should not be converted via `_to_decimal` - if not decimal_value.is_finite(): - raise ValueError("Converted decimal value is infinite or NaN") - except Exception as e: - msg = "Could not convert %s to decimal" % val - six.raise_from(ValueError(msg), e) - else: - return decimal_value + def _recombine_skipped(self, tokens, skipped_idxs): + """ + >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] + >>> skipped_idxs = [0, 1, 2, 5] + >>> _recombine_skipped(tokens, skipped_idxs) + ["foo bar", "baz"] + """ + skipped_tokens = [] + for i, idx in enumerate(sorted(skipped_idxs)): + if i > 0 and idx - 1 == skipped_idxs[i - 1]: + skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] + else: + skipped_tokens.append(tokens[idx]) + + return skipped_tokens DEFAULTPARSER = parser() @@ -1341,10 +1353,10 @@ def parse(timestr, parserinfo=None, **kwargs): first element being a :class:`datetime.datetime` object, the second a tuple containing the fuzzy tokens. - :raises ValueError: - Raised for invalid or unknown string format, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date - would be created. + :raises ParserError: + Raised for invalid or unknown string formats, if the provided + :class:`tzinfo` is not in a valid format, or if an invalid date would + be created. :raises OverflowError: Raised if the parsed date exceeds the largest valid C integer on @@ -1573,6 +1585,29 @@ DEFAULTTZPARSER = _tzparser() def _parsetz(tzstr): return DEFAULTTZPARSER.parse(tzstr) + +class ParserError(ValueError): + """Exception subclass used for any failure to parse a datetime string. + + This is a subclass of :py:exc:`ValueError`, and should be raised any time + earlier versions of ``dateutil`` would have raised ``ValueError``. + + .. versionadded:: 2.8.1 + """ + def __str__(self): + try: + return self.args[0] % self.args[1:] + except (TypeError, IndexError): + return super(ParserError, self).__str__() + + def __repr__(self): + args = ", ".join("'%s'" % arg for arg in self.args) + return "%s(%s)" % (self.__class__.__name__, args) + + class UnknownTimezoneWarning(RuntimeWarning): - """Raised when the parser finds a timezone it cannot parse into a tzinfo""" + """Raised when the parser finds a timezone it cannot parse into a tzinfo. + + .. versionadded:: 2.7.0 + """ # vim:ts=4:sw=4:et diff --git a/libs/common/dateutil/parser/isoparser.py b/libs/common/dateutil/parser/isoparser.py index cd27f93d..5d7bee38 100644 --- a/libs/common/dateutil/parser/isoparser.py +++ b/libs/common/dateutil/parser/isoparser.py @@ -88,10 +88,12 @@ class isoparser(object): - ``hh`` - ``hh:mm`` or ``hhmm`` - ``hh:mm:ss`` or ``hhmmss`` - - ``hh:mm:ss.sss`` or ``hh:mm:ss.ssssss`` (3-6 sub-second digits) + - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) Midnight is a special case for `hh`, as the standard supports both - 00:00 and 24:00 as a representation. + 00:00 and 24:00 as a representation. The decimal separator can be + either a dot or a comma. + .. caution:: @@ -137,6 +139,10 @@ class isoparser(object): else: raise ValueError('String contains unknown ISO components') + if len(components) > 3 and components[3] == 24: + components[3] = 0 + return datetime(*components) + timedelta(days=1) + return datetime(*components) @_takes_ascii @@ -153,7 +159,7 @@ class isoparser(object): components, pos = self._parse_isodate(datestr) if pos < len(datestr): raise ValueError('String contains unknown ISO ' + - 'components: {}'.format(datestr)) + 'components: {!r}'.format(datestr.decode('ascii'))) return date(*components) @_takes_ascii @@ -167,7 +173,10 @@ class isoparser(object): :return: Returns a :class:`datetime.time` object """ - return time(*self._parse_isotime(timestr)) + components = self._parse_isotime(timestr) + if components[0] == 24: + components[0] = 0 + return time(*components) @_takes_ascii def parse_tzstr(self, tzstr, zero_as_utc=True): @@ -190,10 +199,9 @@ class isoparser(object): return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) # Constants - _MICROSECOND_END_REGEX = re.compile(b'[-+Z]+') _DATE_SEP = b'-' _TIME_SEP = b':' - _MICRO_SEP = b'.' + _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') def _parse_isodate(self, dt_str): try: @@ -325,39 +333,42 @@ class isoparser(object): pos = 0 comp = -1 - if len(timestr) < 2: + if len_str < 2: raise ValueError('ISO time too short') - has_sep = len_str >= 3 and timestr[2:3] == self._TIME_SEP + has_sep = False while pos < len_str and comp < 5: comp += 1 - if timestr[pos:pos + 1] in b'-+Z': + if timestr[pos:pos + 1] in b'-+Zz': # Detect time zone boundary components[-1] = self._parse_tzstr(timestr[pos:]) pos = len_str break + if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: + has_sep = True + pos += 1 + elif comp == 2 and has_sep: + if timestr[pos:pos+1] != self._TIME_SEP: + raise ValueError('Inconsistent use of colon separator') + pos += 1 + if comp < 3: # Hour, minute, second components[comp] = int(timestr[pos:pos + 2]) pos += 2 - if (has_sep and pos < len_str and - timestr[pos:pos + 1] == self._TIME_SEP): - pos += 1 if comp == 3: - # Microsecond - if timestr[pos:pos + 1] != self._MICRO_SEP: + # Fraction of a second + frac = self._FRACTION_REGEX.match(timestr[pos:]) + if not frac: continue - pos += 1 - us_str = self._MICROSECOND_END_REGEX.split(timestr[pos:pos + 6], - 1)[0] - + us_str = frac.group(1)[:6] # Truncate to microseconds components[comp] = int(us_str) * 10**(6 - len(us_str)) - pos += len(us_str) + pos += len(frac.group()) if pos < len_str: raise ValueError('Unused components in ISO string') @@ -366,13 +377,12 @@ class isoparser(object): # Standard supports 00:00 and 24:00 as representations of midnight if any(component != 0 for component in components[1:4]): raise ValueError('Hour may only be 24 at 24:00:00.000') - components[0] = 0 return components def _parse_tzstr(self, tzstr, zero_as_utc=True): - if tzstr == b'Z': - return tz.tzutc() + if tzstr == b'Z' or tzstr == b'z': + return tz.UTC if len(tzstr) not in {3, 5, 6}: raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') @@ -391,7 +401,7 @@ class isoparser(object): minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) if zero_as_utc and hours == 0 and minutes == 0: - return tz.tzutc() + return tz.UTC else: if minutes > 59: raise ValueError('Invalid minutes in time zone offset') diff --git a/libs/common/dateutil/relativedelta.py b/libs/common/dateutil/relativedelta.py index 1e0d6165..a9e85f7e 100644 --- a/libs/common/dateutil/relativedelta.py +++ b/libs/common/dateutil/relativedelta.py @@ -17,8 +17,12 @@ __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] class relativedelta(object): """ - The relativedelta type is based on the specification of the excellent - work done by M.-A. Lemburg in his + The relativedelta type is designed to be applied to an existing datetime and + can replace specific components of that datetime, or represents an interval + of time. + + It is based on the specification of the excellent work done by M.-A. Lemburg + in his `mx.DateTime `_ extension. However, notice that this type does *NOT* implement the same algorithm as his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. @@ -41,17 +45,19 @@ class relativedelta(object): years, months, weeks, days, hours, minutes, seconds, microseconds: Relative information, may be negative (argument is plural); adding or subtracting a relativedelta with relative information performs - the corresponding aritmetic operation on the original datetime value + the corresponding arithmetic operation on the original datetime value with the information in the relativedelta. weekday: - One of the weekday instances (MO, TU, etc). These - instances may receive a parameter N, specifying the Nth - weekday, which could be positive or negative (like MO(+1) - or MO(-2). Not specifying it is the same as specifying - +1. You can also use an integer, where 0=MO. Notice that - if the calculated date is already Monday, for example, - using MO(1) or MO(-1) won't change the day. + One of the weekday instances (MO, TU, etc) available in the + relativedelta module. These instances may receive a parameter N, + specifying the Nth weekday, which could be positive or negative + (like MO(+1) or MO(-2)). Not specifying it is the same as specifying + +1. You can also use an integer, where 0=MO. This argument is always + relative e.g. if the calculated date is already Monday, using MO(1) + or MO(-1) won't change the day. To effectively make it absolute, use + it in combination with the day argument (e.g. day=1, MO(1) for first + Monday of the month). leapdays: Will add given days to the date found, if year is a leap @@ -82,9 +88,12 @@ class relativedelta(object): For example + >>> from datetime import datetime + >>> from dateutil.relativedelta import relativedelta, MO >>> dt = datetime(2018, 4, 9, 13, 37, 0) >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) - datetime(2018, 4, 2, 14, 37, 0) + >>> dt + delta + datetime.datetime(2018, 4, 2, 14, 37) First, the day is set to 1 (the first of the month), then 25 hours are added, to get to the 2nd day and 14th hour, finally the @@ -276,7 +285,7 @@ class relativedelta(object): values for the relative attributes. >>> relativedelta(days=1.5, hours=2).normalized() - relativedelta(days=1, hours=14) + relativedelta(days=+1, hours=+14) :return: Returns a :class:`dateutil.relativedelta.relativedelta` object. diff --git a/libs/common/dateutil/rrule.py b/libs/common/dateutil/rrule.py index 8e9c2af1..b3203393 100644 --- a/libs/common/dateutil/rrule.py +++ b/libs/common/dateutil/rrule.py @@ -5,27 +5,27 @@ the recurrence rules documented in the `iCalendar RFC `_, including support for caching of results. """ -import itertools -import datetime import calendar +import datetime +import heapq +import itertools import re import sys +from functools import wraps +# For warning about deprecation of until and count +from warnings import warn + +from six import advance_iterator, integer_types + +from six.moves import _thread, range + +from ._common import weekday as weekdaybase try: from math import gcd except ImportError: from fractions import gcd -from six import advance_iterator, integer_types -from six.moves import _thread, range -import heapq - -from ._common import weekday as weekdaybase -from .tz import tzutc, tzlocal - -# For warning about deprecation of until and count -from warnings import warn - __all__ = ["rrule", "rruleset", "rrulestr", "YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY", @@ -82,6 +82,7 @@ def _invalidates_cache(f): Decorator for rruleset methods which may invalidate the cached length. """ + @wraps(f) def inner_func(self, *args, **kwargs): rv = f(self, *args, **kwargs) self._invalidate_cache() @@ -178,7 +179,7 @@ class rrulebase(object): return False return False - # __len__() introduces a large performance penality. + # __len__() introduces a large performance penalty. def count(self): """ Returns the number of recurrences in this set. It will have go trough the whole recurrence, if this hasn't been done before. """ @@ -353,20 +354,26 @@ class rrule(rrulebase): from calendar.firstweekday(), and may be modified by calendar.setfirstweekday(). :param count: - How many occurrences will be generated. + If given, this determines how many occurrences will be generated. .. note:: - As of version 2.5.0, the use of the ``until`` keyword together - with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10. + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. :param until: - If given, this must be a datetime instance, that will specify the + If given, this must be a datetime instance specifying the upper-bound limit of the recurrence. The last recurrence in the rule is the greatest datetime that is less than or equal to the value specified in the ``until`` parameter. .. note:: - As of version 2.5.0, the use of the ``until`` keyword together - with the ``count`` keyword is deprecated per RFC-5545 Sec. 3.3.10. + As of version 2.5.0, the use of the keyword ``until`` in conjunction + with ``count`` is deprecated, to make sure ``dateutil`` is fully + compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` + **must not** occur in the same call to ``rrule``. :param bysetpos: If given, it must be either an integer, or a sequence of integers, positive or negative. Each given integer will specify an occurrence @@ -429,7 +436,7 @@ class rrule(rrulebase): if not dtstart: if until and until.tzinfo: dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) - else: + else: dtstart = datetime.datetime.now().replace(microsecond=0) elif not isinstance(dtstart, datetime.datetime): dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) @@ -1406,7 +1413,52 @@ class rruleset(rrulebase): self._len = total + + class _rrulestr(object): + """ Parses a string representation of a recurrence rule or set of + recurrence rules. + + :param s: + Required, a string defining one or more recurrence rules. + + :param dtstart: + If given, used as the default recurrence start if not specified in the + rule string. + + :param cache: + If set ``True`` caching of results will be enabled, improving + performance of multiple queries considerably. + + :param unfold: + If set ``True`` indicates that a rule string is split over more + than one line and should be joined before processing. + + :param forceset: + If set ``True`` forces a :class:`dateutil.rrule.rruleset` to + be returned. + + :param compatible: + If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. + + :param ignoretz: + If set ``True``, time zones in parsed strings are ignored and a naive + :class:`datetime.datetime` object is returned. + + :param tzids: + If given, a callable or mapping used to retrieve a + :class:`datetime.tzinfo` from a string representation. + Defaults to :func:`dateutil.tz.gettz`. + + :param tzinfos: + Additional time zone names / aliases which may be present in a string + representation. See :func:`dateutil.parser.parse` for more + information. + + :return: + Returns a :class:`dateutil.rrule.rruleset` or + :class:`dateutil.rrule.rrule` + """ _freq_map = {"YEARLY": YEARLY, "MONTHLY": MONTHLY, @@ -1508,6 +1560,58 @@ class _rrulestr(object): raise ValueError("invalid '%s': %s" % (name, value)) return rrule(dtstart=dtstart, cache=cache, **rrkwargs) + def _parse_date_value(self, date_value, parms, rule_tzids, + ignoretz, tzids, tzinfos): + global parser + if not parser: + from dateutil import parser + + datevals = [] + value_found = False + TZID = None + + for parm in parms: + if parm.startswith("TZID="): + try: + tzkey = rule_tzids[parm.split('TZID=')[-1]] + except KeyError: + continue + if tzids is None: + from . import tz + tzlookup = tz.gettz + elif callable(tzids): + tzlookup = tzids + else: + tzlookup = getattr(tzids, 'get', None) + if tzlookup is None: + msg = ('tzids must be a callable, mapping, or None, ' + 'not %s' % tzids) + raise ValueError(msg) + + TZID = tzlookup(tzkey) + continue + + # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found + # only once. + if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: + raise ValueError("unsupported parm: " + parm) + else: + if value_found: + msg = ("Duplicate value parameter found in: " + parm) + raise ValueError(msg) + value_found = True + + for datestr in date_value.split(','): + date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) + if TZID is not None: + if date.tzinfo is None: + date = date.replace(tzinfo=TZID) + else: + raise ValueError('DTSTART/EXDATE specifies multiple timezone') + datevals.append(date) + + return datevals + def _parse_rfc(self, s, dtstart=None, cache=False, @@ -1580,54 +1684,18 @@ class _rrulestr(object): raise ValueError("unsupported EXRULE parm: "+parm) exrulevals.append(value) elif name == "EXDATE": - for parm in parms: - if parm != "VALUE=DATE-TIME": - raise ValueError("unsupported EXDATE parm: "+parm) - exdatevals.append(value) + exdatevals.extend( + self._parse_date_value(value, parms, + TZID_NAMES, ignoretz, + tzids, tzinfos) + ) elif name == "DTSTART": - # RFC 5445 3.8.2.4: The VALUE parameter is optional, but - # may be found only once. - value_found = False - TZID = None - valid_values = {"VALUE=DATE-TIME", "VALUE=DATE"} - for parm in parms: - if parm.startswith("TZID="): - try: - tzkey = TZID_NAMES[parm.split('TZID=')[-1]] - except KeyError: - continue - if tzids is None: - from . import tz - tzlookup = tz.gettz - elif callable(tzids): - tzlookup = tzids - else: - tzlookup = getattr(tzids, 'get', None) - if tzlookup is None: - msg = ('tzids must be a callable, ' + - 'mapping, or None, ' + - 'not %s' % tzids) - raise ValueError(msg) - - TZID = tzlookup(tzkey) - continue - if parm not in valid_values: - raise ValueError("unsupported DTSTART parm: "+parm) - else: - if value_found: - msg = ("Duplicate value parameter found in " + - "DTSTART: " + parm) - raise ValueError(msg) - value_found = True - if not parser: - from dateutil import parser - dtstart = parser.parse(value, ignoretz=ignoretz, - tzinfos=tzinfos) - if TZID is not None: - if dtstart.tzinfo is None: - dtstart = dtstart.replace(tzinfo=TZID) - else: - raise ValueError('DTSTART specifies multiple timezones') + dtvals = self._parse_date_value(value, parms, TZID_NAMES, + ignoretz, tzids, tzinfos) + if len(dtvals) != 1: + raise ValueError("Multiple DTSTART values specified:" + + value) + dtstart = dtvals[0] else: raise ValueError("unsupported property: "+name) if (forceset or len(rrulevals) > 1 or rdatevals @@ -1649,10 +1717,7 @@ class _rrulestr(object): ignoretz=ignoretz, tzinfos=tzinfos)) for value in exdatevals: - for datestr in value.split(','): - rset.exdate(parser.parse(datestr, - ignoretz=ignoretz, - tzinfos=tzinfos)) + rset.exdate(value) if compatible and dtstart: rset.rdate(dtstart) return rset diff --git a/libs/common/dateutil/tz/__init__.py b/libs/common/dateutil/tz/__init__.py index 5a2d9cd6..af1352c4 100644 --- a/libs/common/dateutil/tz/__init__.py +++ b/libs/common/dateutil/tz/__init__.py @@ -2,11 +2,6 @@ from .tz import * from .tz import __doc__ -#: Convenience constant providing a :class:`tzutc()` instance -#: -#: .. versionadded:: 2.7.0 -UTC = tzutc() - __all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", "enfold", "datetime_ambiguous", "datetime_exists", diff --git a/libs/common/dateutil/tz/_common.py b/libs/common/dateutil/tz/_common.py index ccabb7da..e6ac1183 100644 --- a/libs/common/dateutil/tz/_common.py +++ b/libs/common/dateutil/tz/_common.py @@ -1,4 +1,4 @@ -from six import PY3 +from six import PY2 from functools import wraps @@ -16,14 +16,18 @@ def tzname_in_python2(namefunc): tzname() API changed in Python 3. It used to return bytes, but was changed to unicode strings """ - def adjust_encoding(*args, **kwargs): - name = namefunc(*args, **kwargs) - if name is not None and not PY3: - name = name.encode() + if PY2: + @wraps(namefunc) + def adjust_encoding(*args, **kwargs): + name = namefunc(*args, **kwargs) + if name is not None: + name = name.encode() - return name + return name - return adjust_encoding + return adjust_encoding + else: + return namefunc # The following is adapted from Alexander Belopolsky's tz library @@ -208,7 +212,7 @@ class _tzinfo(tzinfo): Since this is the one time that we *know* we have an unambiguous datetime object, we take this opportunity to determine whether the datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurence, chronologically, of the ambiguous datetime). + occurrence, chronologically, of the ambiguous datetime). :param dt: A timezone-aware :class:`datetime.datetime` object. @@ -246,7 +250,7 @@ class _tzinfo(tzinfo): Since this is the one time that we *know* we have an unambiguous datetime object, we take this opportunity to determine whether the datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurance, chronologically, of the ambiguous datetime). + occurrence, chronologically, of the ambiguous datetime). :param dt: A timezone-aware :class:`datetime.datetime` object. diff --git a/libs/common/dateutil/tz/_factories.py b/libs/common/dateutil/tz/_factories.py index de2e0c1d..f8a65891 100644 --- a/libs/common/dateutil/tz/_factories.py +++ b/libs/common/dateutil/tz/_factories.py @@ -1,4 +1,8 @@ from datetime import timedelta +import weakref +from collections import OrderedDict + +from six.moves import _thread class _TzSingleton(type): @@ -11,6 +15,7 @@ class _TzSingleton(type): cls.__instance = super(_TzSingleton, cls).__call__() return cls.__instance + class _TzFactory(type): def instance(cls, *args, **kwargs): """Alternate constructor that returns a fresh instance""" @@ -19,7 +24,11 @@ class _TzFactory(type): class _TzOffsetFactory(_TzFactory): def __init__(cls, *args, **kwargs): - cls.__instances = {} + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls._cache_lock = _thread.allocate_lock() def __call__(cls, name, offset): if isinstance(offset, timedelta): @@ -31,12 +40,25 @@ class _TzOffsetFactory(_TzFactory): if instance is None: instance = cls.__instances.setdefault(key, cls.instance(name, offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls._cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + return instance class _TzStrFactory(_TzFactory): def __init__(cls, *args, **kwargs): - cls.__instances = {} + cls.__instances = weakref.WeakValueDictionary() + cls.__strong_cache = OrderedDict() + cls.__strong_cache_size = 8 + + cls.__cache_lock = _thread.allocate_lock() def __call__(cls, s, posix_offset=False): key = (s, posix_offset) @@ -45,5 +67,14 @@ class _TzStrFactory(_TzFactory): if instance is None: instance = cls.__instances.setdefault(key, cls.instance(s, posix_offset)) + + # This lock may not be necessary in Python 3. See GH issue #901 + with cls.__cache_lock: + cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) + + # Remove an item if the strong cache is overpopulated + if len(cls.__strong_cache) > cls.__strong_cache_size: + cls.__strong_cache.popitem(last=False) + return instance diff --git a/libs/common/dateutil/tz/tz.py b/libs/common/dateutil/tz/tz.py index ac82b9c8..c67f56d4 100644 --- a/libs/common/dateutil/tz/tz.py +++ b/libs/common/dateutil/tz/tz.py @@ -13,6 +13,8 @@ import time import sys import os import bisect +import weakref +from collections import OrderedDict import six from six import string_types @@ -28,6 +30,9 @@ try: except ImportError: tzwin = tzwinlocal = None +# For warning about rounding tzinfo +from warnings import warn + ZERO = datetime.timedelta(0) EPOCH = datetime.datetime.utcfromtimestamp(0) EPOCHORDINAL = EPOCH.toordinal() @@ -118,6 +123,12 @@ class tzutc(datetime.tzinfo): __reduce__ = object.__reduce__ +#: Convenience constant providing a :class:`tzutc()` instance +#: +#: .. versionadded:: 2.7.0 +UTC = tzutc() + + @six.add_metaclass(_TzOffsetFactory) class tzoffset(datetime.tzinfo): """ @@ -137,7 +148,8 @@ class tzoffset(datetime.tzinfo): offset = offset.total_seconds() except (TypeError, AttributeError): pass - self._offset = datetime.timedelta(seconds=offset) + + self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) def utcoffset(self, dt): return self._offset @@ -373,7 +385,7 @@ class _tzfile(object): class tzfile(_tzinfo): """ - This is a ``tzinfo`` subclass thant allows one to use the ``tzfile(5)`` + This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` format timezone files to extract current and historical zone information. :param fileobj: @@ -460,7 +472,7 @@ class tzfile(_tzinfo): if fileobj is not None: if not file_opened_here: - fileobj = _ContextWrapper(fileobj) + fileobj = _nullcontext(fileobj) with fileobj as file_stream: tzobj = self._read_tzfile(file_stream) @@ -600,10 +612,7 @@ class tzfile(_tzinfo): out.ttinfo_list = [] for i in range(typecnt): gmtoff, isdst, abbrind = ttinfo[i] - # Round to full-minutes if that's not the case. Python's - # datetime doesn't accept sub-minute timezones. Check - # http://python.org/sf/1447945 for some information. - gmtoff = 60 * ((gmtoff + 30) // 60) + gmtoff = _get_supported_offset(gmtoff) tti = _ttinfo() tti.offset = gmtoff tti.dstoffset = datetime.timedelta(0) @@ -655,37 +664,44 @@ class tzfile(_tzinfo): # isgmt are off, so it should be in wall time. OTOH, it's # always in gmt time. Let me know if you have comments # about this. - laststdoffset = None + lastdst = None + lastoffset = None + lastdstoffset = None + lastbaseoffset = None out.trans_list = [] + for i, tti in enumerate(out.trans_idx): - if not tti.isdst: - offset = tti.offset - laststdoffset = offset - else: - if laststdoffset is not None: - # Store the DST offset as well and update it in the list - tti.dstoffset = tti.offset - laststdoffset - out.trans_idx[i] = tti + offset = tti.offset + dstoffset = 0 - offset = laststdoffset or 0 + if lastdst is not None: + if tti.isdst: + if not lastdst: + dstoffset = offset - lastoffset - out.trans_list.append(out.trans_list_utc[i] + offset) + if not dstoffset and lastdstoffset: + dstoffset = lastdstoffset - # In case we missed any DST offsets on the way in for some reason, make - # a second pass over the list, looking for the /next/ DST offset. - laststdoffset = None - for i in reversed(range(len(out.trans_idx))): - tti = out.trans_idx[i] - if tti.isdst: - if not (tti.dstoffset or laststdoffset is None): - tti.dstoffset = tti.offset - laststdoffset - else: - laststdoffset = tti.offset + tti.dstoffset = datetime.timedelta(seconds=dstoffset) + lastdstoffset = dstoffset - if not isinstance(tti.dstoffset, datetime.timedelta): - tti.dstoffset = datetime.timedelta(seconds=tti.dstoffset) + # If a time zone changes its base offset during a DST transition, + # then you need to adjust by the previous base offset to get the + # transition time in local time. Otherwise you use the current + # base offset. Ideally, I would have some mathematical proof of + # why this is true, but I haven't really thought about it enough. + baseoffset = offset - dstoffset + adjustment = baseoffset + if (lastbaseoffset is not None and baseoffset != lastbaseoffset + and tti.isdst != lastdst): + # The base DST has changed + adjustment = lastbaseoffset - out.trans_idx[i] = tti + lastdst = tti.isdst + lastoffset = offset + lastbaseoffset = baseoffset + + out.trans_list.append(out.trans_list_utc[i] + adjustment) out.trans_idx = tuple(out.trans_idx) out.trans_list = tuple(out.trans_list) @@ -1255,7 +1271,7 @@ class tzical(object): fileobj = open(fileobj, 'r') else: self._s = getattr(fileobj, 'name', repr(fileobj)) - fileobj = _ContextWrapper(fileobj) + fileobj = _nullcontext(fileobj) self._vtz = {} @@ -1528,7 +1544,9 @@ def __get_gettz(): """ def __init__(self): - self.__instances = {} + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache_size = 8 + self.__strong_cache = OrderedDict() self._cache_lock = _thread.allocate_lock() def __call__(self, name=None): @@ -1537,17 +1555,37 @@ def __get_gettz(): if rv is None: rv = self.nocache(name=name) - if not (name is None or isinstance(rv, tzlocal_classes)): + if not (name is None + or isinstance(rv, tzlocal_classes) + or rv is None): # tzlocal is slightly more complicated than the other # time zone providers because it depends on environment # at construction time, so don't cache that. + # + # We also cannot store weak references to None, so we + # will also not store that. self.__instances[name] = rv + else: + # No need for strong caching, return immediately + return rv + + self.__strong_cache[name] = self.__strong_cache.pop(name, rv) + + if len(self.__strong_cache) > self.__strong_cache_size: + self.__strong_cache.popitem(last=False) return rv + def set_cache_size(self, size): + with self._cache_lock: + self.__strong_cache_size = size + while len(self.__strong_cache) > size: + self.__strong_cache.popitem(last=False) + def cache_clear(self): with self._cache_lock: - self.__instances = {} + self.__instances = weakref.WeakValueDictionary() + self.__strong_cache.clear() @staticmethod def nocache(name=None): @@ -1558,7 +1596,7 @@ def __get_gettz(): name = os.environ["TZ"] except KeyError: pass - if name is None or name == ":": + if name is None or name in ("", ":"): for filepath in TZFILES: if not os.path.isabs(filepath): filename = filepath @@ -1577,8 +1615,15 @@ def __get_gettz(): else: tz = tzlocal() else: - if name.startswith(":"): - name = name[1:] + try: + if name.startswith(":"): + name = name[1:] + except TypeError as e: + if isinstance(name, bytes): + new_msg = "gettz argument should be str, not bytes" + six.raise_from(TypeError(new_msg), e) + else: + raise if os.path.isabs(name): if os.path.isfile(name): tz = tzfile(name) @@ -1601,7 +1646,8 @@ def __get_gettz(): if tzwin is not None: try: tz = tzwin(name) - except WindowsError: + except (WindowsError, UnicodeEncodeError): + # UnicodeEncodeError is for Python 2.7 compat tz = None if not tz: @@ -1622,7 +1668,7 @@ def __get_gettz(): break else: if name in ("GMT", "UTC"): - tz = tzutc() + tz = UTC elif name in time.tzname: tz = tzlocal() return tz @@ -1662,7 +1708,7 @@ def datetime_exists(dt, tz=None): # This is essentially a test of whether or not the datetime can survive # a round trip to UTC. - dt_rt = dt.replace(tzinfo=tz).astimezone(tzutc()).astimezone(tz) + dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) dt_rt = dt_rt.replace(tzinfo=None) return dt == dt_rt @@ -1768,18 +1814,36 @@ def _datetime_to_timestamp(dt): return (dt.replace(tzinfo=None) - EPOCH).total_seconds() -class _ContextWrapper(object): - """ - Class for wrapping contexts so that they are passed through in a - with statement. - """ - def __init__(self, context): - self.context = context +if sys.version_info >= (3, 6): + def _get_supported_offset(second_offset): + return second_offset +else: + def _get_supported_offset(second_offset): + # For python pre-3.6, round to full-minutes if that's not the case. + # Python's datetime doesn't accept sub-minute timezones. Check + # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 + # for some information. + old_offset = second_offset + calculated_offset = 60 * ((second_offset + 30) // 60) + return calculated_offset - def __enter__(self): - return self.context - def __exit__(*args, **kwargs): - pass +try: + # Python 3.7 feature + from contextlib import nullcontext as _nullcontext +except ImportError: + class _nullcontext(object): + """ + Class for wrapping contexts so that they are passed through in a + with statement. + """ + def __init__(self, context): + self.context = context + + def __enter__(self): + return self.context + + def __exit__(*args, **kwargs): + pass # vim:ts=4:sw=4:et diff --git a/libs/common/dateutil/tz/win.py b/libs/common/dateutil/tz/win.py index def4353a..cde07ba7 100644 --- a/libs/common/dateutil/tz/win.py +++ b/libs/common/dateutil/tz/win.py @@ -1,3 +1,11 @@ +# -*- coding: utf-8 -*- +""" +This module provides an interface to the native time zone data on Windows, +including :py:class:`datetime.tzinfo` implementations. + +Attempting to import this module on a non-Windows platform will raise an +:py:obj:`ImportError`. +""" # This code was originally contributed by Jeffrey Harris. import datetime import struct @@ -39,7 +47,7 @@ TZKEYNAME = _settzkeyname() class tzres(object): """ - Class for accessing `tzres.dll`, which contains timezone name related + Class for accessing ``tzres.dll``, which contains timezone name related resources. .. versionadded:: 2.5.0 @@ -72,9 +80,10 @@ class tzres(object): :param offset: A positive integer value referring to a string from the tzres dll. - ..note: + .. note:: + Offsets found in the registry are generally of the form - `@tzres.dll,-114`. The offset in this case if 114, not -114. + ``@tzres.dll,-114``. The offset in this case is 114, not -114. """ resource = self.p_wchar() @@ -146,6 +155,9 @@ class tzwinbase(tzrangebase): return result def display(self): + """ + Return the display name of the time zone. + """ return self._display def transitions(self, year): @@ -188,6 +200,17 @@ class tzwinbase(tzrangebase): class tzwin(tzwinbase): + """ + Time zone object created from the zone info in the Windows registry + + These are similar to :py:class:`dateutil.tz.tzrange` objects in that + the time zone data is provided in the format of a single offset rule + for either 0 or 2 time zone transitions per year. + + :param: name + The name of a Windows time zone key, e.g. "Eastern Standard Time". + The full list of keys can be retrieved with :func:`tzwin.list`. + """ def __init__(self, name): self._name = name @@ -234,6 +257,22 @@ class tzwin(tzwinbase): class tzwinlocal(tzwinbase): + """ + Class representing the local time zone information in the Windows registry + + While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` + module) to retrieve time zone information, ``tzwinlocal`` retrieves the + rules directly from the Windows registry and creates an object like + :class:`dateutil.tz.tzwin`. + + Because Windows does not have an equivalent of :func:`time.tzset`, on + Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the + time zone settings *at the time that the process was started*, meaning + changes to the machine's time zone settings during the run of a program + on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. + Because ``tzwinlocal`` reads the registry directly, it is unaffected by + this issue. + """ def __init__(self): with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: diff --git a/libs/common/dateutil/utils.py b/libs/common/dateutil/utils.py index ebcce6aa..dd2d245a 100644 --- a/libs/common/dateutil/utils.py +++ b/libs/common/dateutil/utils.py @@ -28,7 +28,7 @@ def today(tzinfo=None): def default_tzinfo(dt, tzinfo): """ - Sets the the ``tzinfo`` parameter on naive datetimes only + Sets the ``tzinfo`` parameter on naive datetimes only This is useful for example when you are provided a datetime that may have either an implicit or explicit time zone, such as when parsing a time zone @@ -63,7 +63,7 @@ def default_tzinfo(dt, tzinfo): def within_delta(dt1, dt2, delta): """ - Useful for comparing two datetimes that may a negilible difference + Useful for comparing two datetimes that may have a negligible difference to be considered equal. """ delta = abs(delta) diff --git a/libs/common/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/libs/common/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz index 6e8c05efd4f06dcb3b18cd919438255cd71ace47..524c48e12db7dfe159f282e4664f71ce249e1970 100644 GIT binary patch literal 174394 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 z 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 z zG_&YHk%{B;0H2AKT{Uvgwt9f$HE-V+O}^G!6<6jo^*NPkD_$-b){w!_6fpD+705EeJyk!_cBI^j#QQ0*01? zp=DrbIT%`DSX$9rKpf7o{`Tohlyg+E)2ll-Ur9p=Uw^vHHm^K=Ta0++4=sv<7G*p> zBF~INVejP=2{S+{GmGeIEMaJC82YJ3jV%mquTkTOeV|&5K)b@wZZNb54DAI&`@qnC zEDKigHdTI9Fu#a%?)wAR9X*B#aHl~T8vh#=-f zkhesTNg@a{F~po0@|GAfNep4W1~HGsiQgrM){#RcD7b1Upc0hOI!dU-4QSmBs003A2~-+4$y)>UN8indXpSh1Rh`CD@>KY)}byXdOFL zf&*H|vA*@~Mb_Fj;K2fGRVj8SD?52mM6~?j<%4BMpY)IOAGm@R?63ZJL)Y+(u&e9b zU_&U#mJ$qpVyV6a_&9sba~uGiPQaW6xJ_h_V)d(Ypr|h%vubmpHIp-A7<~Gdbz5zQ zV~p7Q&i0Qe3P!=lXfux;eRSLzLp?2cgL<1O^&2=%OuujZQO)>T-uxtK{^k675`)63 z-dWUvHSl9x(0deL=HVUR#UUm;LHT5C7XL z>)(x9TiC|O+tPTXF@Pjz^t$xq+yGvNT}UBR6`pCh4kyOygY#$qdy9zY7_ZR$gDNKa z1W=?!M2J=c6s|i70E0att4*ogYBt#AH?Yzs^~=+@Nf&WgEo#?$%5{J+FB>l0lRhZ) z?C&WsQML_lE@?ZOJdXo+iWXi!9LP_tTzk6SMbfll*l}H7HFROQ-T}cf*_z!w>A9WR zf6jh)pt4)0A8BFvb+4^VJF$Ty3t@n3|L72z{dh0@{mG@CPu}Cu#OB%}^V5%lL##!;Lk-EHf#js{ znyN`2EX95IOYDn9J-C-H_|81or^5pm=w_|qY@2O)weNnq(7Uqz7CT>V+jMT*dT!eh zZrej{TSm2Z4>dBYtNRUl41;%QkvaBPt%&~NQ)`L?3N!IhSpqX1iz%YK9up!VV2k zg>Ah;$(G$#NSE^{1=mph?>BS70>H(#&`CtuUSf&70MgG?nF#wnmpTC_NdCbyRlv3= z;;`QZ?{p3>KEOD2!mDp2KeFBqQk%3?xIc;Cym2v;zWDmL{B3vjhnW9=kM#M`__8mv z8yPf_*bmjSj+?$3)01->+YAPDmOZaunu>A-7Csg+VOvoC$7&XJOA!(_!+-+$+8LfB5;0x4a{mJ+EFiHs>$U? zvCqtwUd%#d&m76b=~wKdMo*-C=&xa}sZr^<>2;dV;~$jspHT+yTcm1e0ZWf6_U1yD zR#X@h#sB&??@iZ?yHUlXtYv~G${&)1bJ%r!R8cxxj&^#&$A07DLRk>W5}`~931#dv zj$zmK9V^Uw1SeKb9LZspp;}f?)?BiViTU)Xk-9Zm>GqKt#dsm(<2TgG5J)I@)cPQ| z!2K9&y=MwX_chk?ZdGa0lz6d)2t;iT8VOj&%-;K}aAXFK7=P?>U73+)tF^siFalD` z$Zcz=-9DRuQf7gKN<&ZFG}dNN6FVrXOT}GP|hwWXLppdH_F)`&a{EqBZNXs9keRd{s@D(*h=Y$ADe=R?x*Y-+%$AjkH}tUQlt8E1n(WmLF@ z2fu8fr~eK8`3%^q#vNxzqDtP;t4@4s^ZOUK2#AY&=X7Lv*Vmli#=$KYg>L>BhqYJ7 zp==l-Z4%IMcNl&H1?+7CEm%&ke~ZojE9Igle^2M}+NLEApt*s!yVUV|X9PS-0n50C z)J6WL;gUy3bF2XGdB))4HvMw)a94EzZ*U5T;G2Xr z$7lI@YP@Vmet-ELX$FXo4k0O|2)(L9K4!DQ(h0x{wTiO}@LZ^O?T?V}KAHua_QB3B zi@G0^nc?3h>KWP&4_Dwn(E?bPJ5Gh#tBz`4XOtt8!q(3WmX}GEB~4o~_s#Q8>{hZF zo%j)UD*aMUNe{76?){e41`RS#LU?`Ohc;sNR|VcycUx7Msvs2t>LHX99R~KoEqhlui zNzb2Qp2PX^>DQWuS$#^+=TnZ?c@z)pkE<=rYe^+kk+!K07(n!-JE%_qei1Pt7D9vh zDvahjDSu~xHPy*aP_|Y!kpF6DyA^xLmQl@knyegrCc2!X!)qVjGNvOhT{j2R28z58 zw?dq@3$<@LYehVLIGl1f`2NieaUK}$b;zS%%s1C*ds0p59>{xSG`m;7zg20?)-Y8L zv{t%cSi+ofgB#mI{H7U9=ndCVbq;}4d}x^BRHR7>{g7k9`tNM=49V5!i0#&|t)~sA z;g@T`>-3)&Tha_Negzvt;k)bWHq8T4@@=O%-KT%%@VoP~yUm2&%bEV+$TS>HvwptV zy|cf%*%w=yB;>0qL23dI0G(H>=z&`UB2h9N7pxttHh({F7+$NKd&h!&OT(vPr|GnN zPDQ`@tks#UmZWu7t=c{4l!pAzFCkm88+T0Xq<53mO-iowQ_8?QdWon}?}(v$A1jn8 zRXB-gQJlqP*Mw=w&F>OHQX+-<1;Cd=rl*kb(O#v1r4T_rs$CPJy{d0SXEsO#X;ix= zMtk)JjFuSE9Vz_AMUa@%S%YD8iPP*UUEMq{-*=+i+-sCRf-M6?dB1k*pO)5L2+x;o zsZfUeg9<=s9)uFEJP9aBeer8Y68dm5O#8ZE!vL9jx32o~39~pEv-}HAGA?fNj2S0b zp#VdIuo~@$U!PpGSvk8kyUAci;!Lq*%&9Lp>Exl>{G7U4NhnA>D}^I#9Nr{jg*4x~&@-o_9?Mk`^-6-m7 zljOkvA?&ZCqWZr0VO&xW2}M9a1f&E66r^ha=>}E|| zo4L>R{rP@>YyH-G)_VTAGxxRkew}kB?m1`geaA5?PxE`p;XAhvY`E-77VbZ5ur}~s? ziZK108Qj9ODhGA^ufE0ak7>;QiPjevVWRP|HS1$H-oR(Gm&Khm-f%?M)dNj#-OyZJ zzWde>LOxm0u=5fnya*m3xU2Xn&~yh`KyL?*(HFFiH9tPfCRL2s2EL=92NysmO4+#_ zNJx!0FXKl+c>v;P-aA+Df^A^(D^PawHRPLG?m2Z4ad(mMuIY@sO-mXQ>m$P8{-zj_ zo$UtK*}7G6RoPh2h?hBPVR|!pog0>up|kRCqe5lZdWHUoD;0-`>D176oj2`?0ChZ? zv&!X%TaGz+6!7E#`1@X33SSYpoxG=J%cM;x25@kR58!OIKwMEx@h?|o$OZgBy(2XO zv05TXc5YYdapvLG@h2+pGU5IBwFr^O*lhHh>xBx;LMJ5V_1#6QnvOShgQq{o9$$5W zFlmD9`JoWUbjG-n-s(7uu;MKzWyh$^Yfq+@@1K5hyKVB%4qOFIO|&T4UhM3t zo%(s4k?Kv!vSqmRpU_>(l*Hdjw$k#-HjYNUu1kN;=GOB#ZvN~z!R7=32JtyRSk8CK z+h~m_Tf!QfmQZ%YLoidrmJUj&IaP)rxLMm}rb+)H4a}fib&s?4)1kmA#*WDVZNvPL zY3|{YZJ)QWQ(xKs&qHJ!`!!ih<|W_Do<>sTZM3G3Y<34N?q!OGHA|<_&Zj4TSj+6s zhNn+TGFlwExWm7t&N50r+>{+{LNWKe&v?~krTdCd^h=zS&_#}j!-x?Zk8OpB#pwg< zGov6IJ@c{dGs?_}q0;=YQ~ZgwZg^=|TJ`kBaZKf5iIgcTyO@0+(%P0CrK`>%o^`K;{*1r- z;&*stRmh*dQ#2yvE|*f{;Fqd4Bc7bLD6O<;CS!hlVC=l3WFEfJoSJ^&7rWx6GW?=N zt^bW+zNxfOnK2pFA4gA3Gk!hjw4;1wO@rn14(vej&&lRu_Hx>?RthhJYzAUZYb_>n zm)C4&yU{&1fy1^>{uhmX&BqS26-4Sw(fE(oP3)?&B!tvwZ>&{GHa;?z=Z7U|e=p(W zeq@IgtLNqGpy7_dpHsCVe3sLuBqfnd(pMl;^@^Oz_a{BRAE&P7;$CMEib^Hc!9O%F zmeS}WmR~6(>&?AHD~aiN`Xd70RzAoVx#;0c^0uc13L?IvNsWi0V`qWSOK{0+e1G!N zX7imlkPrF(6d-cyLF>m5^LX)7gy^gX4flaKwjJb=Pb9{RNK6_dw1+upIBpNbU-?Ah ziZhZxda;P2#Tv$VUBA%FJlv3*K5mzZBuZ!92#lg(h<$D1;D-^79#H((r}_acM}Wdt zpGb0XM(%Z!@KYJz^#`X<-ATpr&N9<2GZ7tzs_d*eT5sb99*NHu@6F5O0vvaUBcL51 z9F~()97q)CjY^MICB7@0LMgXpX7&OP@9X(B+#S>`^5|+9mDKkE`ZMS@;twED3PfGZ zj*n4&?~_%3IPX|ykD7kr(g%|Lp@{-Yy6$Ype=uD){kUBGdpMPbQYlJB-nU*_TlBGA zQcQb^T$m+IY%Rb;Tk%n*#Qd|Y1ATJm6<@QwJ6Jtjc`Wv4%Tbf=pJY5Z@suo8-}#Iv zTt}q5tIBX4zpbrStHHoW!dj0U?}i|G^cwgc6Uu?56|Ql}`Tb+{KOT3aY9ELH_`OIN zX!d*aUN7c}lo5NG_~yy$5gGrP*ao9@QgVf;w&q{tgN-bH@Ls1pz5sv>1-*jXe_RHN z%+<5UE6^6WhXL%Mz##p7K1fTs%%RgINN$epgw4HO;9_kGExZ2{2DODr z&4oh~Hk*wRmFKvd<9edF<@)8rA&IXCSP~0cpGHeGwX>n_?Lzy-MKmUpEt@O1FIk>v zmNkJlHuP}sj$ZkVzEi3?NPoQdT)o-MlOC; zDu4|qA5|x~t*&59%^$MzD6`11C3^NqZ!RNNkY(%xw}1VM%EeE59uzi;JJ^`%Ytw%J zOK*fGx0Hm%OpEvNWblnPuFmghQxh@yUi1Wyt&6kEC%@OzbckYLCcX+(?@0&28%RuI z^OOG2_W_}NkjNkqCjyOLfkrZ*Q4(k* z9%X8h^J-);SQ2Cn0TrHu3Y?&V*)v3_RY0iSdK~!<4*78}E7!kKAlL+YdQiJp*pcO4 zgeY2Q&O6Z1K*XV09#iU0j54}aGK=sCBwwp~N z?#A_(%z3_b6ji*k6yEC|&L6z1ofvL~dc0gAjpe>t7!gp92@Vdp3&u#Lrao(bRk4{T zvnoYbOs+y7<2i~V0ws0+5dla1H}!0u^Z_HIJqIgvw0PGz zI8BO>1(w~Rig`)xc2K$Y$>GK<=7PxXaF?aK%!M-%S6{|f9V7h^4p@Oyn z$+_`^@dI-1ZeU;vz`<{<2gpd@>>?&j0OXdO{en+C%&o`Ad%1-DC8_WZt+RMtzQ!*! zwTOfT*Nz{x=V7m_OR%n=E;H3&)%_#>BW@1Mm44*Na_sCtRH?^e63*fOkZh zp%^QVKgg^>v@xE{@ewUS0{935_!kLDcln48Apt@J0m4L?%DwH>IFBBPVKnN5K7#x~ z!;yO+hDTF?hAHgx6NlgrCeE+F5A=P0J|_5sjk8O{dGkPw_QlU9L?7GH1kl8o)%2xU zX$r6~TYP>#Cwht02-_DcS{0Q)&z+Pntfibk`)KpYxazvZ zfweBjhW!k2s@Me#DdVy3+Lwdho+E9qVTD$5^TudErOr@P69uo(MLy@mBwtz{nNt34 zPQ}X#Na{=PZ|d|$`M8~hPbyuqm(_^ioe zGB(P~oq7&%2m7(zET2wU#QX-JkO>P~*g zmbf3>1YT+|`YZ30wLa=B%WWB%Eh{}l=J~$OH2Xhu`>tcW19*IgfyB--bQz$1e-(WW zB~=A#c)*E$Q8lBi#s$3IQc`~wSQ%+RO0=AuT&kD0mlBG8lbqD2KGdak@wz%axS?@z ztKWRq%!7sVzwYmzX%vVU6u)2IGrBh|=YRdax&JPrIQfL}D=O&UxAOHKu0IXD_Sm)Z zxG4@5+@CGv2G6%U0EnL&wHg7+6@YTdCY*(1eNXq;Kni6VMoPE%JjK+K%0ct;@Fs3) zv-|!=@mzGmR-j7blD|^bwrQZS&ciZk2{k!wv_baE->5rTFJ>K2r8$u z!^p{*U{1kE1Tw>}i|Nsfk{X@g~k zA@>tjQ*mt3=Y)6%lKm64Qa}B#{Tk&13QT?M+(Zc}4$tFHxaaA2}jF*%xzhKmd8DUkwp1LomMmQ8)|v(?ovMjF*dpL#%fddG9qp6R^G<` zVE-npar~vlO)IR{z+phtNZGN%JDYBC!hO|sR*-RcB2T&AGX|d6mp{PQVogk~-sDc& zvbdvSdfUOf(<(U6C)j-KzjWO2)HL5YV<&GvJ#QG#V7#f5m)AM1LAiC-VQj+ss>lI` zq+&$8v@}Lk5-Y(u6fZb#W@gu}-ZyMbZKa|$ZSMG5CJ1_1tDP;KR;F%l_@Dn=4^n6& z=iQ?nP0IUYALbMSZDxLRmH6_I{L7Cc>(|nrv%}zgBEex9CZ|~EeUZK}vaz-g^dr{M z+?|QEsWyn*) zi7)z2?&a|e<5sUQ*ZE9yP+B%m#DuhR>$thztUUF&ZI+`|ZE?hD%Op83s`{nYTpPLV z^@j)`tBg5wjhX*TN}2iBS@!GNbK^W&Nsj&)vPQ)^TYUy)SC;Rapt&H=M_|uLKG`hF`@tZV|iT5`18B9=Sbw`XX3+H@>PM*M>jj({B9q8Ur6u z+k~*_pu8PTbfE_do1@J)IPdUUz`ze9N^$WNs~Pj*tJKD=H~s&p(i*oq{!s}z4|x2e z^4yL0IDY!#^8@FRh*R(t-FbwZvcO~bxtMUXB50W?X3?CQ@b`g4!NbTlc{?9wNI4cx z_k%VHnnVmt4;*rTpU+Q-6wv(?A#WeyXri^fp_#$L>?pwc`Ub<#98!*t(~Z_9Al@xR zGb7#m90wB*mz>!5GaXSVre7T7EisM@dYcN3_9IMn-_NW>p}F2@z z#K?E#I6N3_W;ELPm{qb$e8xpPme@@VW&G6eEep=>Jr2@_B?vS`g>17cpIF3L8}Tit3+P;Or1@oL#uzh3WF%YU~;ay>Ej zYHR1(cH4C9QR=dSmpI^ZCYrj8->e0+UWzlh--=eJt_aP$W_s4HzZ`EcJ4=r7V6aW|$aWe2wG@o3 z@DY62xrnh!fNoFbTOSj!Z!_kk=pyY~U-Vu-W>F`(wC|8zaoPx$m}gnpq%h7sn})q= zgJIv}TfYZcaHBaCA$s!Q!?OoZa0A{@_ypsKlafF_U=xj^eF%8)gaPv55z#36ht3C2 zxFH`16q$@iCB|iSFh`x#NF^5p*_{lI!XY1?Dbg9MO7^}A$&0Dn&f3X&)9w?@Nh3)S zj^F%-;Z^wIk=MUhaL+T0SLnXM;)NF-DrX!2^=^9x0CW&2^tcCdCiaup;oO%%d4T3V z@FXQZF4-KGGsOpV-6{VYDNCaAhj-y7?#Xng?xVolVu0Zurhw>(vLd_T0KFwZ%^17_ zRdl!mpRS@XW7|Xc%|Hv9QLNnj15wdm%Vbe__x~xM0eb}~LfO=`cwGss2k?%Y zWxF|mZYG!5bz4)1a3C8L^;C z)X3DkEd?>AtweEka38WXvIWb%v9nV@A3v(v-{dxXZ;e~6_wTT=wA4}yMSi@oo_ve9 zx`k?yDU) znp#^mAI@Hj8(h8_=-*4nnsr^Qs?s#Lw5kf57N)e`a2}Z>J83j^ENMPWGFfvTxgiA! zI{w+vvX*&VIMbGSOf~vU3-5geg)f~~ZYJKa^AfLQxY|x16(1P{QIu9PK#y_yv z^Wk;{j}IE)guGY$Z#LQvgsib#0&02TRryBq#N%U@0YE|tKa`F1+!##?gorLM3jg1Mn`+Df+(PPU^g&G|fGcSTw?{F_OaKK# z7l;$N>^Lxr09Cd@DR~IMsvD1;(ktAgvToEobows^tefF?VqsJM!Y7qC%NNACEdvH` z2}US0PtWyz1)$5^pEmcJ(2ZJKivkCyVdR|9+M8At#@P!e zJEn>!bOY;}@=oLU4df>oHMPBe;^FpE%R;&l>~0Is#%W|CW}AM$zdNNoPcCGOW@W9A zRJvUaIyd*l8s6!H?S3BuRuq1u7cix1Z!26hX(*%YzKyTUiot}q2Ki6T8bmCOqnUI6 zXr-r7S<5YIGv;^QrcC78xG8C?tGmyoIDEUrRo~n`cQA1yHg);no34Kr$JMh%?LIt3 znAX66civ#hD}bC6lo&~=8+3!A-g+&?KHj>U6n6S<8c6Z}E>6vU`a;zJ^`}?9F7a1$ zaDTS*#zCIhJB==J76BGbRgIu8aktH%DTu5v{Olp+1UQ?jzoIH4rmo&$YwE1^B+6Lc zJf}2Qq&B37`typHHDYt9fEU~PPtN!TKL0Lw4Z9RV5~U!P@A(D)Wh>syW6W&d&)h_V z*nW$Uw=ZzwG1{Dd7L1?t{2E6m!4U7mNlHe`Gz(Ce^ikpYOP#MI&+hfj0zESoQouy? z=uyBA^vs+91w~(#kK%d1Ag@_z)wO7zk>bQ*iJKR$wM~%XbYY1*`>GhwOpxJVVT%X) zs(cmC>xR7Mq*XVjiA{?Mc(Jslv-iU_f$1C^>INK*f!Oe8tHz!nOnYmRl&!+F-GJm>mfp0o~?EwHn=?+jU zWS@kSujTSmOmu!{i3(GM@~8At?-+~)DRFeP8PV`q=Y0*^!^(-V~Bpr78h+=D-^Fg3#N&S4bTFT9}GKxhL@|HmC&3|z$KG+a=u z0>+oXWBevQfbzwgUHG&Ks_rDz3SKyNhl~Sw2bd7Xp@Km3WW&bCg6oYeh4I#4)1li| z0n4T3i%3f#{(BzAR`U=TyHt2cC}iQ%6PVblqj7jsyYg%thV^|DspsSrABy`np}A?u zY05HxZ~xoVt%g;pO)!Az;^K<5-3;dkoLwArKjlS6q@|JZ4eBS`z+4m~_4Nsh@@!V} z^%q6lrk|k}1xthPJQTeMUcS5J7hZ7MyKvA#RFe*~>Ug@WsFpR~RTyil~)*xcXbno@asg)Q^S$FxoS{{+F2i}d}C<&=`Mq%Kvj z=i69sMT7TgUuVmZbL@T?&?@VrsM9WGo`4P3%tMwC*OKB zidy}_C{Waef^PUdB-CuS4MSzPSV(OD|HZn8&{UM3HBJ+JB;bE#g&!LuCo!+2N4H1N zHGbz;Qehr5_QAmXH2S4xbLa~_^~*H5DrK!_JRFGy)icrhGbcN5>h5I3zZ5YLFYvBk zur-g4_AI(**=E8NJYXromF+1u{K$))v8AcnV==`rFl55A* zaRYkpcq5ZlUh%liFS2(iV%T)j-Xq&uD;F50xAGSla?%FzQZ~@rn1cmnFdB%GEQ)>E zbC2g?Go*p;f?tDGnwE1m=M++M*^K((){wyV+AT|vuVsexsaKJp)4nuM2=pOLyL^Cj zsbGO>6y@I@oE@~_H7u+*bD37RQ0JXTb}_%Ld4q^H;QPFj(*K*=K5(k~y#HsQqm{oj zhuSORp4lw-<;=(5D`&o?x=m0~@#VdB*Gd=6bCdZpn8$p$!qki>E##8G>7ZQB#)EV* zp4gnK9gTU&QX7P*3fr01Ay289<{7SQeaee6)>~-ch;ti9q8TEL|`#Uox4Sxwiz zFk7+St7~8)*&tCexKh46(93_OB>QPKy>Uvnpi?AwhHTj;h14~JQ`qe%Tg9~k19M{) z+fJtp!)#eA+jWP*kL*!0o*u4B&eQFtS{+u51!ih$=G3&s_OwQOy)SdF9VP7drYkQ7 zlwj_2(e00sWD7~zrX(iu2P&0xhgHU8)@lx(CUXw8BE{?Rq18kB9{CQ_O9r*gZp3j- zr^fTm%ylfMT4}ZVTK-_#64Lv3U9xn-@gjOC-@o~khSa6K$}^8}!x7wl^P=~|>#9^) zA1}~p9#iiiv@)Z-rCw0%n64Lcj*ZB4O0Pa-@uU~=i?8=nISYAS=tDzFw2tog84?-V z*c@sW5NZV>BET6#6BDB8eTa$XOG8Wa0n;xO0!IJT(8UyJdT}wud})}8K4AG32ZWYG zB1v&pqo+TwGnBRAC>Z!hB{pNzab8l8#7RHT>@$3;h9PEk@;r0_Li7SBnwT>y=AEGt zlTpA!zcUCCHO>hJ?SqLYCa)d@5UFH$bw0om`x*X}7>gzV4afe0IE@bx?un4X>)zA1 z6*NYEV(CHOv5DHnmV&-NbfadZfdu0aiK7Q3KA?R)p~9t#CaL`H*&jT1#{B#sd< zL&VvJChmljbo78$1cF0E^a?Y82#r<>g7cIp3p3y?8m$rp=NUJ>LJm%n3L33ufI_(s z5t}&UCrB_E5ju82FdD59B$%AY4?Ca$jn)bhOi6_PFrXKW)(H|!MN2G1^C2PjZ`ib{rC->>Z}PCDv0g z+ShGby&W!1L@J>jGKYIj)zVo^AwRu$~2DxHOD3ShXN6qrjvDhVA?G zc((G~0fVwjZL}>sQ#+j#2o-YflCI=* zuCqei){9vGl|MO~7yUylDEx})9Ilx$dL=2`;9tr_t(UuOFQN~#8ksbG89Z)kotTr~ z%q5-f1%(Tr7uq|_QKdOKM@go0u^vCL7u0! z*KI76iB4`crbi=hwn>DMQM~6(8TU;h9IzGq3Pi>j z$I=*8Kk%af5n&Md5t(iL9$Z#aARZn+8rbL($_WarWxkO+_l_YS~u2Nc$*t;x`XQ& z4`+@p(w3#H_NvS~1TQqNk+oi~9WHxGcbB`u@Bz3&(<$KS+QmjK!?AICyqY}^TO_Nw zj9EDkI%v(lT@pq?AKxO&yFD&>++Z{L-aVJzH;DWouc>~nm8!J)S|mrm0^iZu{ufET zHDUsGa^b-io>wc8bY?RvZU;}pI~Vx&Pv_-{TkALGzes0$U3j}m6dm+5W-NN_Pb3}m zP#!FMc=j9~K&ciR>#iCSYBOAW8ZP&y>j(rJj%zU2?6=C*<*AkVgaM9@;T$%ZD-(;= za$9L-7cBJ+r0$#`|UNmo`?lc1kMdFf$}eIA3E)^+-d|kK5MMkug2dfj5VB#|O=* zvQ9EW?VFEFzqy}uO;z8XV6(l*;B-A3Z0_wmiY-{8JN@>dMv>gX#K7F?XD*+6f*Qs6 zUEXSiUWtqt3U9;nitN$S#kIxljXR3Z%Ir9Ykcp_F_X+4Cyw(cMWsTTSYCmUw>RThe)4XHHV~k zc`kM7yMel=UqQ7CArWMj^rH;eySEV|O=j`!Tl!9UBWA&%CMs7*UL)}zz_Ow9YfJsA z881RDJy$mhgkCUj=&)$L%Yaps#F`eqWZ67rN9pueeN6htKJO+ZPXZm>(ie*BsrvZj zALIo>+#-P^O^Hb$v;tPOtM^~Y*8fT_iydhye|G9dCik!Q>LBa8M3L;3wNv)I&0{xk z_zG4=%-UT(E zzX}*FHBir8H0P+f&m{hV4F6iwK}pR@m2oR`P<_bX70X7RM)pRlF;vvbDXn4Dt7^kK zd;ZAy@Nh|)+*_D)!Q0rj5M{8oh>}$PB^(~qd&ic4!tQ6 znzx9ohKFz1K0Yi>+N{kJE+f$8gOUrm2T~-NX=S;a7D;5iila5B;Sd)RrHeSHOk<9pPU3;>&(w8$2YU^w@rD~jN%gSwY=fZHf zKz?3(-V`b;33paDxXQA>oqBc|+o{n#QIowOw~zPB>-dea?q4})cZcHW86j!KABQFM zY4wVV7nZs7@EK+2_X62`={p6(LXCP!HI-#iC&$c}`&Ljo=QVcN+!d#U{!Nxgc$alf zeu;yrE5h6;eAwAEp6X*2ohHkyq-_De2*2F$6cf02-F$3=B~k-9H?Q26l2E*qwyc`O z%OI78&{Y#Eo%?C-&&n}ypNvm6bmy*?t0+3!U_>Q%onImgj?s(4qxvOHhCZ(&aZa%zGPb*m$(IFJ3%~YqD-? zUvvRop)NV#JvK;K2r2>=c6GwNLyIntys)hs<_)qO>Z~>D=fSf9>J}weYy{&){D&8; zFTwVS{IaWClmqDmVT+-w3zA6A`Vdr6O4BwO{$ZB-!h&QO2)x|bWWT|RYueU%1RG`5 zILtC6Im~kWc$nqk$U9`@+qA8@pvdL+m;L53Y+Qk{K4huHj%;xnypK(3GK2bq$FA8N zW(_<(%z`>Ok{$5WhZy`LR0IW|4)cyknda5_WZNMETl$5$YH)6 zSz56J*`n$SyTCUQ3BMfC0&KSm(elUFF;|-Z#wtynz<0&ky`Ml)#R1?gaWCqq9W2R- zrO($OR@kQ#9{bEg@dP>n5dRlSfa^~JZ@Pil>Nn>%1pviUQpW$ezjR08?@;Pw=b~KZ zwZgfiyNG0o4nK}0fQlW=OzyMCVo++ykvoS{1GmTb>1#lVt9N~6xiutw>|$-i+Peqg zvRvuq@a5xzwy{k96P`jf#Yp&KsM9@h3K>hQ^?4D%hUl}<%;1P z+#LLVWX=kp{3vAqzSxx``+m9EyIgsMCpS=d>qX7i%v$Mt_w&7k1cWOd#mEp$=Hm_C zxKv$zj)|Fg&!4OvFjDYwD}kr+NWLh16_(!s@ZsJ)W)kmJ!tT`y2jaQfU)c=!2o!ky zALqH`Ep=L)p1PowzSu3i0;tpW8_c>#49@|Se^6zp_m?7NP0;NvULZAzw{BvtHNk)C z;=bTVC9}c%j66xnwpbN;$=TRH%V(CXmk*Bi3R_I_A)D!G-O6l$tpLgU`%H{oi+=V0 z<%)s%8-!K)d?quF7GM%eDnLv@N}uHS>L4*@!b8R6hcNf}JN@c^SylfcJ<^KXaF>#6 zg#1}X*H<&XGf~Urd)?!IdA0wgvh4jrG3AaZ4cO)6Sb=!9jlrOaNg>hW@Lnl_GuYIf4FG4x(l9U?hGg?BUeJ<>goS zbClq^?th=rzcnU?YQ$874=zfXQ9*idCXe9pcGUpsf2nT&e#m^KNhqzK$?>bb1vPh2 zJ3H3`o&OTkJc5PkxXM>^0{(B9>;-o|K&LkDVBrT$egddW2sUq^_6ELd&*e=SCTafn zA%lMyMED-OSXh{uoB!a~Dd2Dpml!0Q2Ji_9NcRwBeaCofOzB`cL>~##i~FMTqVU1N zaROW=EkxIB@8!LM7tqQXc5zn8`|g6QWQ;(lq$p_Xd^)@{pfE#uC&z0Anf)^CkjcwU zqQHKzEZb?{iE@qab~+sS`dzDlyEcorO@{qyT3v2gLW@P}!uC}EiC5SOJiaMc!}wdz zs>Go?tTF7yB54f5U&d425GOU||11AmvFo(0)`Ixjt)`EQ^)ZLxs7A>mrR}vS11cn5 zO1uZQfNPt<$rx{K2SuKHni~~*`%_O>L0#Wlovn3bkKHccyyjS*Y2M{@b6*ZMaJyHS zqiOHRKRLPX1wCK4g;|W@KMrVwN?&6l>xuj{TwH%aH*=`cwPjAI(!GDu3I{L}t;kBi z-h3$9@9Y$hL#9^CtqC&iuq=}oBD!HxkFrN_pmXHk1K)x`Mkfg50@N!Cz2eC(A5PY{ z0{?6Z(ow}<4a?jE42enD;p3SiV1LRVEhhskmXlZ-L|dUrnt4 zcjc7(DZDmkFy}1PTxX|mdKl1Y_h-L=uYAcu^_;H)oMjV$SNg)TzCOZK2o>*cv6C{l zbFKW#bZp9USwX+pThKk~Vi6K_xcePguPi6Wap`pNETmIe@m%+;A!^lT9h2RMj`|1g! z%{20s6&tkv9^LPDBe^&#hLKhhuHAC?Z5j84(h@>&XwT8?Z{4?%I^17qo*<{&Y5eFs z_ZE1$Tb&PMSe6A~E!}m)`o$+Dj%4PZd!K^(>xNq-*7VjHDLq3LjK14OQ5qhxIT+Hn zO4=yJ(I-C#+AQoa)XuUq;HtVXM;4<_V&F=D6&4zb^O-y@+RGee0DOIhLdfhJuWT*1o z$9j*J+HTZB!UijebzM5$xtq2P*?LT)lEMA#+3MB_oDb(V=JY*+irh{stD8H;?YtAH!#X$=-7xMz^Xq$nWOZ zCsYSDF?6#WMxQk3ecfMW-yuGXuRZp_&9hH%(q3gZuW8dCn|}(Eb;Li6PLHmaPqwm4 zILqq>Is5g;+{2o3WI?`=_lMCpj&+?gefnxlhw;zl`>5XKIV3#$36gx+U}u(EWiMR# z%U+n@sXs=0^O)2z;ViyQ|0%5N^Lv=ApzB=dyObvS!yj#ShtVQa_42x%i%$&&VY0A- z1qOSq$D57`o%-*d`84I=Qov-{KJJXrVtv#WRSVt61-BanS2wN}NCIRTvDPCcXeekm zfEA>#3y6N201!6aK=-GeD{96+_j7lO@04R;Go|e>itzX?6KY7YC%UIPedYqm6A>_} zoWzya*x{wN-}HI2#j^m88{of-=mkJkI3A;>9>ZD638qS-h$+1Lu9WL%fHn6WfJxsR zfNM>W?-1{4ee*b=KZv9|%y<4gNlxZ|A1Svc?l?su%FFIlfgp0c@^(uejMx+K2AUI1 zl_vdq5><&`Vu(HZ@Du$>F$8meq!hiVaOD;0w#K&*?cZ9a_1n`z(TPG#LVu{0NjfPw z{_`}UlwZ`W1B`rXV4C=NnE=3{2hiP(-B62Vp>Dwkk%`7MaPShLH)nYB=c;Uj!Mv<` z^lIYv$&UIZry+DUWX=?eTyzSTc`mt#>Qi*7!77<5uaUT@|J_&Dp5>3cIlnlbokQ5? z3GZ62k4;5Ov0#Y~YzwsFuX4;OACOK#0(fZgY-z+_1iu9uDClt9XxdRf7PbN@ z=tBuK7MQZTYKp~925i(q3ntxo-O?<-AH}u<|9we(-iv_#WFt~*8}Keg*~-*QPQd?{ zu>y9)VJK5$L6gZB07n9Vhu3HXFv-gl^n$AwQDtAcVuTF4JZ#s2g)k}e0#B1*R~VC#!`<#dyscvb zBCt=$>O+a4*hR6!=Fizv#-0re&YKU%hTQ+YN7F(91@7=efp|fI(%dGk*9PScG zdm*LWhI3ru&k4V*1}|?yJQTlqTtw2%!8s&7XB7?|57i5ig3tjl zA@Ik<$VToZd;>Y=liPLkoBNc(a!&f@XP{+uTeiNt(dwe^M`X|$t*Uwrt!ZuFsIQya zSt-2#-B7;FyOj9e4rT4!ClSJh5er;8Awzn_5pzNiO5xeedClbNclT1;hE;p!Yn~YA z^~V{O_PK25t^9*HaRAwKE*Hoe#C?`(yA>5$R(lC8;#| z&uSx0OfRFzS_Sd{=soWPBz%$MX(i37G*F_kMHY;ug2_Y=6r))X|i4h z_c9rc5@KTbd`2Tuc;JT*d5eyt`JnAF%?ui*gq;rqzJ7Mckue}8k6HGB_UXCM4W9GcX>lPM;qC>D6gf8aACq%> zd}{G=_4?Pee1?Z+LTo#K+;qosKmh0Fao%aQ(a+)W=!|jG^7MR$G2E9U_IQD@)@9! zAk$B<4Xh05@k05h)$6CA#mj$`fgoks9!OUL>I#5`5x-B5ZLZ(t=#6fc|4Pvnl9D9K_ed5L1*=T zA*NmH;G;1X5o;@l>3U>EXiclJziS9;rt{wBUN%4Z<<=1KG&a==Ge1K^Jj*cjPsJ@s z2vnKwjcd`sdeBv6iR`f8S zsx}#|@MraSf6_dLaKnng(=USI{i&H-Z9hUDia!P_Gpe(+ zY$-@>s$MAaI6J(X6l)i~>YKb6{HxXt{+o%85*k2Z|6gp*jv@SYJr>@`wZ#R9 zNTCA;`QawELq$KnQEEGWj>HBwr@{)hLqI=%PDDGt5$FBwPbS{TcVNl4U%1{G;TXz{-jdMRH&s= z_6qQo00`@%2JviCoE00jLz>e|5E%Zu(; zY?PMMc$);8cXsx#Jn6LZodL(>%G*Ri4%g*bhSrvby+Omg6t=CQ#cx2*#NzGG*|Hvl zqPOwZCzJu4>za(p?wqeQ+1XKDKi0>_c8vmD(?cVF@bR%XX{Tp5?RPX{)Kby&FunFECts;YWY z)b)oH%#T-0oOh=4_`g^W?U)DVJw`fi4vvnba1Piy7)$>@%)Mn?ltJ4!EF~)_EFdMd zv~)Mp-5{MRNQaa(ODUZa5&|Nf(nyM=q;z+8_p;}E>V4hU`{jMVJfEHqvlGWL|C!m} z?s?A4QNHT~?6>VnYill>_V)uxMyGMEhqF`09tVSNn}a^{?jHWlu8WBnZk8g?^qi zPyd<8e;F0=iDC7(KfkriQ`7D)9mQsE;~0x1`P5{c<|9+DM4RRGEsqCBrsU1hx@H#XuM{)Pmk3gq|?&=3E4tqBgxe zb|mo%rtla&J`lzO4S5(e{u6tQNzxGp!9o8W2or`{UD6G2dh`VP1}$Xy0kyCM1Q)uB2I^CDw9aX9(0Lm`KU@8G2p(fyhg+#@L|Nm%!K!Y$tw(;PfkMX!q z(4-zbN`;_OhlHp+4us0UWbh!b(WS@(p$ae=e8^XHDb_%!3QUFovVtxp8VJ>pV#twz zFi{h{grO(Jg)>8cVTClHQhy&WctwL9@&}c=P6EOX&A|>iMx}0*fN)Y1yoQB*jtl34 z2C4MQkV0NRl*$N%TEk?XLB2keDh-6%hZAceXXI&PGF6|Mb%fF#G-6ljfBWV4dpVUD znTv}Y%5uGF_-g|niKsXRXcM$}s`H2Y4osvGxb^zQTu5S~r_bn8S$_fl(a|QKQUi_C z-U@M7Q7|znPB8;^kA8RpTd!Q-y^HGwCf^|IUB0$#<@Jxljk8W#&Mne)cj0NVqsVH3 z+Z^vW;9{Y+HQcbQF_!r9QDNcTbz3@}>D5V7NAuxYW_`&a!5>XM<*Q4+A*Z8dWNk~& zGXvb>%uNBpVa&N=)%C3jhT&4q<|tXEmsc&e)#|{CqiI}e-E`HTh0`6M$2ak$hq}rM zQDiQmKPrHWCydczTc+-j7f&V%qi^i^4vG`=&Khjf74IjjJjtlaKH#UP=Pqf!A?SM$ zBK+VhG(?4--7M&=tu;hN$hIbTvx`jFSX+yr?`2*t!^>=DdcIVAllRi0F$hGqny>f~ z&S6RA4+`xl#=RwN&#Kn4zE2mvMQGa5jC;gPq6WhSozg3wRhjDHp`#}j_b(o*PY%C3 zv+zr3|8&tYFKWP%{9Qx+Mu6kBkj*jOeaX22>uWq^ea{4>ZIWf#nQCg`G4rW74M2M; zzIuIslQms4uF`X!o0wQ*mjpa|@&vb05Ok`-ehD;~o{F>NvdjbD+?=A^{C(8Fv9=Xx zw+3DW=?PesZ{}(q4SmwO7aAOYc$se~JvU`byZ2~t%(&27Y`fq@bbw%>tW+btF~x3B z%$;-)yQ+rBh?3h->>lb$pDI^EpE?lTNhTn?nu;CH*=$bp`OeyEetjwCN6foghc&HD z{@LOKwL9wr+%GTvJTfMPo1G;NuIK&B#u}$|UcOh}WSA^6>CSq?zRI^|+0VX7Nh#3i z)7ER;f!a}FAG&$Ow{Xlcn|mQB*17%T;x*f=DPF#J2VV>;R5fjnya@QtP7iH0TMyj5A?gM8}!uhxog2& zeV%nS6;2G*h_X)nv}9PO9Tq+3_1yeg&{^=vB^1m5bO6nxaGB{XC5ax>+0W}f9^Cn_ zLG_|%KPi~6jrh)vLpuMp42zyEb9-)5E3c)7hCa^p@>Ez&4OI!YqV&9{e)q+Sa+N;Q zD~uClkOJG^KO7dV`2<#sH&R2j!a*iEbnl-$xLewpUVQbW-}w3rgnvp;&^jBvq`kCQw%S4f5>!d6LX&R`R} z`J8ay6Xd5YqW?I4SOMg7^EXtYNp;K-6^pk{uKMq~kQ{_YZRj!PtY4C}gAv4M|8_WA zw@!GsW?Sdt3z-R>HSae|(^lZ_7g_ht8@E&jWBYsq|8DO;AS01*iV4s?MiKwreA*hY zaV|bi?&f?S`v+Q00yGiza73x{7JBOXbJ*?Ex2^RJpS3OC|3;jiy2SpIeGp&MY;>EF zC8ofi23$0NMiPV1+q*M^d%6?@H~;J+T0au|n{8A=dsZ*QB@q5n?{ThqGD6Rd)H+M3 z?s&6@SibzTC`Dvh1Mgk@DEZv&gOo-EH-p* zkc?hu$|^Qq6~_lSnpsqwpAf7S1~tPEw2!sie@X3^3O0< z697dlv}4@)I7+H&N{W3An+L$~Iy~4QC7>Iyzubu-joAU^JxKL`hBlz@k>FeSM87`* z2QV4``{Ey_|6z*;c9H}T}*(!VFsgq1JfA2oN0g_)gl`r9s2-n1?Hm^TWC*6VqgBPGV?)7jV&_v7ee}g)?#iYxiB*mLA zD@!eF5xs3w<6Fyku32Lps`ZW>9v8}XE4eGlET#dv70u1o)N-$48n{ran~r}$R5*P; zB0lzOd#yi8iOetgt}NHfP6kcE3G0`a9h#=6vq*`L$f#6S>}6*p6fsXpjQCR7uhca( zotO8TzITC_<_J#9Z31sGg=FzMA$6<31JIl~kFHxMaAI&(Ruj-;Yiv;q$Q|8Jg;?6F| zbr1WeJ^uHjYxd>AZ-d%uz{B$o7_s%2R2LFnI|JBhpL9~|{Z$=bvJ32p;fom1PAjE) zWyhD_xv=|*cP?7;ZeF03vwPFY%yRdn8)7uvi*~qtr8fJJ@o57srm(%HbJ1*^8Uf+o ze;?ng*jqw<)7JR>eaG{7X11vmf8+Altn7uD=nd?PNAY2Hp7?pVxF&pCnyAZ5rey%7)$&rTyVYr z*Xx?y@1~{!tCo2Tt1QQLVZHfTLvP@|9OvkT<-zB(4<(;?s(wCo`8na${$-L~^{N3C zYq9F#(6mi!0^I{U3DuEZD<&{{vZpxq751|KJxyt?UHH@McZjsr}HsulV@YwWx)$apqt+>+%eFE1Zd)7i3(yOR2)f z>6C37-R~zf@vPidT8GO3M~z27_zl%x&ufPYYW<~2h~eQ00D@a03ef)7)GqRd>VI5! zn0Z`?kHznP{-lF_*2Yl=;1SspU5ML`0f6y+tnM+;pE*tGZf;*(>uya(JUR_(mdEXx zSIl4VQDaV%FFI{&Tyk5J`{?%R=zV*jP*VKpZqfK< zXs6+tVKrs-{z7V`S^hmP4mP*hJQ6eV)~t3Gsi*dVQ6cFRP=Jp7 zT*^Fc7w@sI`6Yv5N33r}jHi7PzsvA*K7I~8vf7!_7*KRlUDhl~F;>yW&%a0Jp5esE z=7^x=D<7ARtY0rVF>_1A7xsTcXWx;Dn;tzkNtMh%SjNx5;cL|B2YPQ;T75Y>Ui*I{ zjZt8tN+GMR_E9P#!zHyj8KigL`6rHHdU2pg+u;+MDCAukZ}48KKSsa8lGv|w%6c*)H{uPXexpkCI#_uGn95q05k?;3-RV5jARm!p1}vjcuc1PH9|REy{=`n`-v?dnARYwK3jQR3H3Xsgq6F~>{v`Xw-8u!m zmhKfqM~{V&rUWb#Kr>K-^d#P4L;O+Nxn4P(Mdk0vn2OJV0n|aC8UGw-^vrI}^1R$H z;_CwX&jINH>aWCCz##Q&&}=mF9_Tx>Do{w;1jK&ad zs_ya6w~Ll|O3EUT)K)EKTq*W$(09_~PJycCT{OREjdH9?vr**keViDbZ83k%+k8sD zGZFKo|YLYuWv2t2y^-STL>ug^gW{i^gK9Qe&-+P<+ z?DX@7cz`il;6A$tQxv8eCpJdVEMWcUaOymK38~Xmw(RUTdid1gbgZJUzD-amy~?6Y z^x}1Oz98?67>8~e5szx7SYCbNHWfYN6Kndx2Fa06A{p@s>bR3ff^9}F`^^SfUd!}0 zlV{QAi5~B5s#7S&US@e~DyN@OnV8flrMu3hxZFk=vwb`AaoPN%XsFu+|2@;9sy^)=^a4s-M2>D}R9NUn(D#!&Mbb9x%AutHRtD(fJ^ zNOrLiTRhbUsYDd7Tur#vnxpyGfdx6T=%#2=ZVxj0XC7o77Ki+oHuDbWaX;p+Gn;UT z;^!TFnVsvMgNGI#9wpI8fz3^w3v!i13*ElqP0_J#hy0V)^9~JQemL1M=c%yeXkOgC zAeRp^zz&7=WnAk=bk-bcgT-hYe>;4KFxI!{WXwN|baNpaKF$!L$pp*ZE@Z5yAaNTc zl1N;IS3bb}EWsXJiGoQY31&fYT*31X%`tyG>4v(HMbWSGlf3M8;H>*I_%@2VzFlA1 zF_g#-7VEj6__n8>uXGL%hpW?U@K>#KYK--0k$~EO- zcf#Cs?>GDb`uda5R~5u}9mn7SRKpW*Q*oe$6`sk4(?$Ki3I{Go4kkV>2gid_pxF&+ z_RiY+UDB=kZ^fe2nDI10RW3mNf7N%!EJd_FCD?`mDI>ris4}R04oJR1wDF(8|7x3n zx0`oAnJfI5C43MYRN7lPM6-?Z;xCulDpclXF38*qTEZ(?_M*lRIkvY9~}7 zGPP*;Bq#oJYUPX1(n}XT3t4~r|9NtaqZ3cCsWD7oT8JZan%@pfF;tKGdv?v`_nRdZ zxT*i-+Za;fK$fC(uv@D$_vLbAmP)wb_E50I-E%Eq_TUs_rr%^n@{ z=Q-AZ;AnPLSa4Uv*DNI_c1hkk?67avXu%N*&$W$7e#*(eh0ra%xWag}`uv(PKx?9H z-7S!Zdg3a>du!hNh|hl!f8HjZqrpCfh3-``N3KP6$yhwP+1BcvvB{;1^T+b6gWX1< zMbf%grOK@Z!Dl&H4%3eM-uv)Vu%vK(D69NtTg7ZOvo<+5`*myX;2A4g?1ww#{xJW$ zhgvNUr3aaJ+f09zG$V@8!c^dyw*1NZA^HSKxTAyy4&<-5qs&K2=V(De8J+k~ zZm49mE$D<%B^Bjz=;bA4RAJkX&}9R&nV^3?VG2qZz^o(D#xa8ay1|55N1p%Y zuz^`eqm5f4k|(-RBhVyWVTCy83xV09(3&2UT{JjS5X<5LX6^%s&`%V_S9Assq@Ix| zQU=69U>2y5q6bp!p4Tb~C?WR8Wih$Z=%Xn&C&US)IJt_pf^n?_v27B`f?*6cZ|J;M zVQF*`@fHziHVMo)xr04s*9hF3>;5d`$ckEddlkUx7Cr`~_wXG8)f7dS#cM!?7(hj9 z_8sYJpSS~5)5}3H!G|oU)R-|UGJo%RrPk4?sv*Fd~?M;lR=NYC$z>p zY~ojWaI%fG_m)k=m3MMe=V)YpJefi}ebcRjLatpsE3#S13c%rnuj) z`sK^k@(&(ICkEF{f&D;E``Aw3Fnt!;s}=Q!z_2U^M;^bwjoO3V5Y@$3m2#_D{%Q{< z7}IHrHTh+=3+y8bb!VPxwp20Yq`iG7>_t$ z87-Z96`l4372^1vX_hUs6;*!v=?hvg*VC8M3oEGjNy;zVNd_hge%ZCoF}-DII?XB` z*a=_Ro#*77P_WBWNz@O+k;|Xe@zpX3NF)W*v-F(Mzs%(6IdffOdQ&u4L#dyO{TO=4 zf%G-FD*_DU7Mi!%J0AaBYE2ypiq180Ui02ASn(Te5GP?crc_LckhWI}qS z4Kfh}HajW0w!FYeTMYNS-D=@RyXoNCv<|fS+S|zy#Kt#ApzUI6SAWTM%4=7L>1RRH z-m~w^Kg@SeQy46kZF-p_KB0w!8`o3AoYHN-M;FT`LKp2rr$K3U`jM9!N#yWrb?vls zRX$g-$kglfmOepvz0lF@G>)JC{Dq(GErp?NTzLU~zMV~v?(R;zqT%h3@xscIfvadz zjyk}tkY33wKXm*)!E!S;f#*VkiG^PGJ-ayjV1x~`m6casMt7de;diUN`P1}sM8j1L zA_3UNP<1QI_G}Z|T32#7ywyARqS~KFTpm7#D88TM`N?OuqJz?Q z!^~lZc-~FaZ$e^(SUAv2Z^JzGu=Bd1VGJ7EU~kJOv0DCZ_LkR=>0H)4x;6MaWgm=d zS1=_LSrJiaa?*u18F5v{9D94$>~Bf@4vGJRw7#@ZYsksK{d+{RHjR^HM^bQbaVY#! z>*TRmF(gb0H79NQ{bS_nib6V7ZL0OfSskMWbDk_KzJ6uky%e!;<9LC)f#@vaalWB zS%R`?*+y%JP8*^_yK%&X9vi%wd0D`Ws2`I;V<{!t#;T~BB%p2gV_T!)AlAQs#NbJr z5RxiEclmP~-;Xw0kD(+=K>njV!WZcIY&|m%oEZKF3Ur%+iJP`LB{%oG-RBzznv7BP zvE&+y6?F@qK-bfawc6rFajEY!~z#y20d-nS_Q(Bm61Ot_tnYU!p32 z)vfp#kSd>aE6#eK8Skz$36BVC%m!%X)9W7BtCp-n-xy{2Z z&Ej)~2aE5=j!x#uTSn&mUu70ZueEswi$q_dr@Y7yL3cX~No0K~Oks1lV=OAYQ^hz$ z)aV~J>h8*8G#$h#Yw9&ilquFr7H{$~2f~zh{ zr+TfyF~7UBMany;v-9~Hqn%p5i7}rWSEVfXdQW?UlEl`?g4x?AEE=~34AZL}-As>8 z+Ur7&4o?|AUS821{yt~GsJ)_d&}~MGz43YYg7YOQqJ6NA`a?(JSB~+jC+X1g=U0Aj z$rh`>a?>1T)x{6C=_Sa&uu*2e*H8>^ol8Ew@mS<4%RG+qa;@;NpE1dF#%rc+uE+Iaa_dVi z@>;PPq0HiZ72r*zLwB+6V${`@x8^r14Eq)!iQ>Y?jC&?Y)D7G}>WYRWhxOF9S zn%LBn?dZyCvqoGGkwj4~oxVaeOhuloS$#BHUXIwf>^gM9Yb%*$o|Dq1xO!7~8c-;< ztWGwiNr(Fe&4pibL_Q(W^J|3sT5GP_B6AAGp08;=SboXn zdxu2(s^RgU62*RxFM0Z!wqQ0SnpGntTIKsAG9i$_{du?@r!MyE8(N9)AqTWvAL6Hf z@h6aU@F(bl=`)z7!9>yt*1_}{Ow(W@=>qFrS>jxg@zeC)<{oE4`V8LY>c_)qb23i* z>|&UEGES^xwBB#*T5=fkJ!RDGGxg3pX+(1v>*VlWRKDgPAxJL?%hYdBJ?~_Ap2Il# zhr#>Jn<*P*rP{;j!m>U|&&Vz3I2Hm{zwsI<~0i+FIjvnc`_ic2jq7 zQAT-077q+xZZ^U_>-iAr4ozpdS7q5>>L}3EJUz(gEbg|VKQ#l0Wr|@DWpSOGRH=*4 z9_ovj-EHsnxz8UMKCl>c}q< zKPSyWknrojm>F5Qce*d%Et(B(E>OeaRcgb!$V3)KI$O&7IT>ia_=ezmHc@d&>aTq1 zSu(ijpzU#+_-t3eR`qSYmeFkKWM#P)D(h7dN-Jt!IhWCsd5;Q#ULM`Ddio_J;6$_o zwBD|7uNvuo;@ap#CfrCG-CcLguV(8wPolTkQnW_;O>u5hX>1lV+S1=D(UMUO3@t>V zZMe^@Y1Dd|Ho8k1JL}yc9g%odHexwKp){unaL=kPEw>2WVtl**IoCq*tb!{@B+o0j zKT}T{6~Bd=zGOm8UaI*^BqP!LnK33>H@~c3<>_cVzlds>67?vDA?io&!ZdT~c&XF3 zZsTEAJUPMlYCSUZ>3Bwa)nm?s7vG%C>_ryJ+%`rI=cq&$%;AS4ndytH`?Y)<)<+w$ zM3)A{9^=WiHQ$d`HA(M3s&edSJxj-<;&0%jZncs7<#9z8{sa$pY{zna=^`vXcSS)~I+r#aimN;UP?8Zw?4{*VK7M#{}V1`)QR}?DWaWCQT zv~d*8Ij>$g?HYTvNTzD0B#W&mim~N^ce?h1MLY>DP_Zng<#7o4$t@F|;+y-YBUc}I zxe`QCs5PVLUP>qsLR~OIEKsP8Bov6Drx+n2&TXj{QFnuMZzXz3p$XjzRS!dIxDuMf zFO0-=hJ8qqbJa?hP6MeYoxHM!seL3AsGz4k*ruP*QK^HY=swI3GYGBw^DELos~{n~ zsMN6%3UpBJM^QhE+V+=q?u|p83)a??zoKP*+$)=s+hzrMuCqfpa5SxcmnSpWEC&$h&#K8TH zq|4xk1{rkDI0sm6`X{CYC;xYncqor8$RSKtdrB@t+XR8w78DR0OsU1dI8T@bC8P*b z>M$_Q7pFntGBC~`woVmh^Z<0W26M!an8yidpywQQ06njvLF+MLj(8ID3;_)~VXy-u zX>=Qp=QIj;DbztVPfYQ0TV931R7E)g=fWVPqcYiY^#pji^xm>@b%unJhGBZ>HU!wR zag$77H$N=dIHJ#BH~tSa3L=?c!L&d&ZgUVSX}C8=qX4>>L$~ny_I(5C`!$eJm)Ho{!JrggZXP+IuEoMjlS><Q41+lofs+%9fQ;ssfVi}%e2rx;0dn=ac)9L6;6n95&~$JeMj%Hc z3HYTBfMDL>gzLOy;~1{PjlM%h(Z!uvz;D16Y_$vnC(}=(V8o1Ci{Gv{pYxX~!l)Kx zGIQ=Hl1y7>aa^EL*!v33->B=&Vh8+$a_DaRl5c8`>r9*n;5idNyZ*Kb|6wyTW8+#3 zUV}9PPHrKofJqnPXm%dBy7kO&!@8tjIHqz1%+A`q`CT@HFZ7bEret17@7a zSpFU`%<{GPQhZvndsh$8Rs%RC+8+YWd_4dPp_vuv)qV*@;7?Nyjy%$QBVmxN|7wm> zT+-BInWuAXMqMrUIxII?a6e8*tCEA$p*zO| z&zeJu+YzU4B9zJ`g;Y&zXgfV0XKrH)(v%DOz5A{Ft^bTvF{{`4@=6tR-Ew$GA1v?z zI)m;&FjYlS%YA!{d-s#1z5U_%ptJ=TCYy22#GT7mV|bNx_2Hd_;~%TI$yWx2i<}55 z!*qd!jks1{OOPxq~SAQoCqpJytpm={+3BrF0gXE&fw6jmH-)T$jQp-o-y zTZI2(LwTZ?&8%3#@kUF>r9XxF6G;3rf0~E2k<6jP1hQ!TW$6Q1^c^p>CQh}@i;d-q zO+j`HG|jFkFgzT7o~>nW#OCC49X?te{(AnRM$)2RKim~{hRT$+g>ke+aEd)0RkbE~ zv^LN*@1r^&IO^bIH|Qv;yrScOr<_32u28x>Nyimz^r;iaQ59dhflrYYkF8Gw31ApC z=qN>nag7S9tt1o#BTsH%INvj)eHtAm=_P9p;LnAgcru zS2UPMQyAHul=A5Js6~HfG<0Il3J%PR&Ri+WmC-|CBF88K-jB8Dj~ zyI01L6?Q`_5NB-MZ`eN3c&q3f5t z(c6lv&da-g5#UDdBva{p-?!^)=%RA&BtL9ttX~C`EG1bw)IVpd^Q8ptOoiGl1|xL`6CZC=VEThryOq?);6~V{0=b7IfZ%s$BE~edLZmR9i;f2eGn$E&Y2yB z(vcsY;nn?_7JP0k9W(B(QP1jL#l37T2!EEDc^KI%mM6!+aFZkFnZd!Or_P(MT zUThML+wZ~9d8J3tI=8L-_=`Q=;Y}n5Q(;AXjZlZcvA&4;AGH3AVDaxo(pXl4p{;*( zDfnN9y)@8~rdR}BZ_*rLyIVv)1pT3jjIWO#WiXAX7czzJ-F4T@I!1Y&g*e&I$eGV~ z`1r0VBj2d#BbuGwIDh0~a<6qdz5V)8cwMN#@AqSOrETV|@pEchTa7>-HZB!a5&of6 z%6SRZB~Zb)@Z!s^XK|A2EwRr8X-VT9_oDdgfdQwL{`s1o-e)?SuI%u|&x8)wRn@v= zLeXYd8#w9a>&X;un>}S(XUe9oM7&HI*b!7NLqbAoUz(6OTg|cF#UHRoznn!H7GX2c z3lW!B7B(w#5w$j7nr>`xY({RVBK&7ZBNxGIF;iZi*EOH{;sfr(SscEvcqqD+%s)jt z@T8>H#D(0VNVcDC65ll#m*L0~UQnnddTCG zvgBL{L}+z|LJ=QYg&x9-Lai>L@D$385u(kNV2na-Akj+%9mNRoMWKEz(Mtl=dKi*% zavVhW1Y&?DH5(8o0HcdJ5ji2D&W(v*$ASDdr<*~Ox($dEhtWYHr07yufpO9>Iy{I0 zy4174IQiE5cp2^l6;$e^sL(F+kT{iYfxnp=Y@Gn|2t!IPFis1${uH8!A*CA_rwdyr zgoI;ASq8=#$|V@V=u+Y>h#@;~>25Hjf&$}S!*V~%v(WWOC~!lo9*6LvQ4dQf@Itwt zglO|5KWde#B2`8=qh5&W3aW^E2m8C*#FQEejB|roP+T)6Up}~9+YS+*OAhyz*tKBv>A`=om9fZ1o@V^4P{6zGtTE}dNjPuc$-Y6Ec9dt9>Jcfz9;L$`3v z6+GfnfSAB;2x!qgU7}AqTe^_F5(H1zOS>_j6#GP!{nRQQ&5B-c)=qht_Gh-r@wJs8 zz=J~HCD}u<)?Ytki5QR>!EF#`*84nr{<~|Mbp7B%$^!6XDdR^F&6@*(tTROIlF}0% zG(H{;9<-K4omC8Xfvux4Z=yGOP92x({q-kRE z)r@lA6IZ08-L|KM4e$G}WzM6h8{LlY=TxbSM{^H+)yIVI6D=ngeA3(yj6}JEqbSmsB0Aw<*dTlw5x-oLA(ge;e*Sz(l^>{d1j7KEI>L z)KIF8`>4SPi%BTkLwGe&n7FOjW5zWFyete}ba%}ss#|_<(keglYm85bhEXa~rCBxR z*^ELYj#`pAjsKrF_fGL^hP;WZt^3bCSnf@Ck-7o%=0K>HdQ0%4-5ya+b~@A=h4pzdSP-CmqGT7(0MUG% zAH0@Ej9&IL)Hd_9fv49A0=bVOWBUmrsp1*(yf=RDr6}yieQWglEj^p~=jyy@$HTfv zvN1FHb9o+xQBeUVqs@+pt9vNiYVbrmLhQaD2Hp~q3w6vXrlpE24UMDal_u)X{6IAD zvg8+KeA!5Hd6v@mqa!oPrQd1vY;AGuR2f-9%sWyWw0eJ=@rkAO$DY~9mS18CA-ktW zmRVvB+vgeuvjzsWvl88f3b{cupNZlFI0hI=q^O7MTJD*`aNHZQ?TTT#xp2x9?IYB>f6=5b(|g5JlGTrB=I!OkWg8dZwQNC)hsh07vW@ahVtB)N$#Tu{g3sY%aEi! zMo^{1_vHA`EN>z{`Qki`{Jd%&3wjarl2tkRrrP<`sD&nf+KPbdgMQ<)eWHIEEAhX~ zo|vl(Rkri#`}8m-Z7@H(*^)$&QQ=A*{)$dhzTLBjC4nbc4%1m~2uciG!_vaU)AKD0 zZ^%h!U5)U#Cby+tji{i*)60mLH9k>S>?FkO5`LHqqbYw*xJMF~5Z167z`lxAK(`v9 z=Z>d0pBLWnCYXISLPfQcuzruGT!R-+&l&;XcHud9Ir!pi;5o|{gTPc^^Zc(Z4H`Ku zT5!DKEe(1w>)p~|#M851aV<;?W0_Q>DHqI%Y*>{Ap^kQG%Joaa8&+*hItkCf&g2QQ z#+7pT&Rt|xVJw?UH07Q$M9y94;QT6fz`6aAF+Pa{r{SCy#&Y;a)_CM4f%6A_dDK3} z)d)JVP*m^$2WAHc#skqz4LS*x!R3=r zfXk0#SdF-H184pU2N%b}ho{#JE}KRV zYzS@)W6=Uv%Q`A+j92jb=;yNu+sWJ76!@1}z2mo)&2f+XO0_V=8CM98P>%%`j+YPy zQn-H~@Jr`FK0uY0^d(7X(DW?=AOxr=NeNwF1K@u4$o3ZK+Y<}bXD$O;cEfANb8cTW z%UaRQ8>zX)nKJ*FxOL88@IxGZ8)a0zvd*$6wRoYi%aK%iW{6AKpv;{xX9QH=hFtoo zcHIUu%HCFHPWZWavh1uIaTuy&G#7oD_3SjcJ^D1d(`Na@sP!Phkg)l{u?)jbYu)aU zKeAidVXt-FQ6N8U@^Fl4^0G3^?f|ImdUhurPo&2?H+!&bS~NH1e@H2!X8od?)zLJ3 zkA{qDqOTwKMZnjNM^wU8S)@d!r2^VG6*YV(dBz!i$xnzMg<*Fo5Mzg7KkSrUTWH$; z#WmXXH%H|jm0dS!-j+ILH+AFkBIan!ZjK~2(O#sFWM>_|{2JL`?YE73W>3}FD>@g- z&P!`zUl-=qPP&uxx9#|Eo0t*(?SpCNk#H0(*q|KNAhF= zZInImA7czXb5<4`vmM0&K;_6t$}s}2VlUGm8^PZL&qt=;I_~FgA56L5Y8B_u^~QuzCM_p|Oa@yImK~8c6nc>hwF_)! zcl9L`Jo58p0{d=?H*94NL=G;s6grL$0{15UgCn7(2bf|GJAba9*u7({r*GKL7NLX9 z;cK0yHI2nvC~vKX&YN5cnta_mdgrkq`irm*w!pSTuz{s+W*^=AV}vl(vxLvKQ&G?D z_<@~DXHU64pf=gCDQ>vh`ncvj%YxNT>wQCQ53(#d(>tJPYO>A9n?ro}u-fr!SMo(> z)6uGXxqpY72QYq9e*Hb^Fy#7odB{>_=vkM0e4wPHn{Xl-%NZHr}!Hf7S&~ zqh~%IPGPq?D~NSy z!|B>m7H#p?c^>^i+H;&DAB%_3+lm_)P5C zHmh;mR+`y(*N<1D)>FUuE;9DFN)BI78}h-YN}7$E)b}?-d{>SfMV~1jq1`kkU3u7c z`n=#Mr<5?0>7T}Gtzj(^ zoFot;v~~-mtVJa@8Oam$64LOQjGpVijDk?s9+Z5v_V?tkM4_cUD4S^Q{^S}DVA>Bb z6CXhMexk@ekRlAwcnBL0jDLy>NqQi~B>2Ebb^q7HuH^^z!P6g}^iiX7g0N3Rf;qJC z$TjeBS|3K3OlLorD8!;=!Y=rL@+R!b2NbEd?<8yjG{|62L*=*9yV&hDC|f@*(!PHk zjC!)oCJ*A#qe?{wXwbp5!-EC27;sh6%1n`Z2B%)Bp5fx0rI{<`D!SbZ7fkp zjPsl%mQtZBK!Y1L{um;QCN)8#I1`{DAg3h=(~gNx#DUaypmd>0T?A-|!NzeR80bMg0FO`wL6oR%?6n;5dug>tdc4q@WNDddDIMWNX~4xw2K zU`G2oAecQAmHS7cke8N;4{AH8@HcD%+ShrBLIEfiPDljW*LCvcgfydaDk1Uu)FrCR z*AM>tE)pLGuGBj$m-Qxq^fPc155Kqv{L0yV@&%=*hTpp{kRLrZ;+&*k8f*6&xyy7C zu$H-Bmp$^yJYI<8mCD>vp07@p>+bMXOy3#_6Py>=4xA`&r+Vz3h6G%mJmhfv+3>aQ z@7VwjzU%zhSAYV+4?xxdK2m&<2?6M*90L>?zksdC5wfDVW566VQ>1dn0+tqN5I~dK z3edHM<^BdC1n_3%PH`dx zfNNg+YYD6XQ&>yt%!~*?6zsVowgTuig#c8$y!XJx8UVa`4t!l#j3R-z&p2&+{^^xf1Ay{9NCOXRN*ACWnUKK$o_^pD zfvgmPZ^i!Ic}Dsk_|gYo8E}mAC1YTtaSV7pMv;1XUQ&frma!ke zxP$k|q~5{J1`+kTUBv0jf3?#3X*qm1=Fc7SPkxm#?MB{vl*k5h*^Z=&_z{Z{T!?dz z967za@_aodI~?Aa=U*;zve{bR`H7`ek#F`#f>>WA4x%}KZ$6`t{_rcu`THm7%kNl9 z=%$V{gXd{aOMb}JLEIT*Z|9SI8(T%_y_;Az-VyJ8*3F6^tFWRls*5IX9Tsj~=W@Ah zDkF$YiT?&4685@(>~d;a@An&jD)Eoi!pkv=^6_k+1)K0-W1F7Sg`M`V<>z0_=W&2r z_MVpu3uA49D`}OsHGCaLvE*I?cAE@TLMNNLTc-=_KZo_Jt21f0{Qjhg*b@pJYI*q2 zSC=Ca&<=wbt{Sl-%-n~CQjgM_{DXZK<;{&hU^{vYbL<`(axx9Jyi>(ak@Z~KJuhDI z@B;V$dqzJWeZnknkE!QxTFT1cL}t7=R#@>`@e2(rQ-kZxXyb5eib*6wp>NS;m`;?0 zwAJ2pWr+CEY~Pk)ny_KCUrBJl_5@K!8F#ciqftlf2X;_t##w_N0y#7 zs60#E&0v*CQqL&`(n#(I`Zs0Ab9=Jtn%Dqolv)6xjL}WC4{5e%*-<%|eem4;7{DBk z=N8_H%?p*BGAF`wJE)7uwo8>oHyJjN{X;*GvP~USP7k8I*dvlm>7WlP_W^O9(V=qW zM1z>J%d+Qql6ro4o_NtRf82k8^by3F1QVW{dv!#1WPjie*s=h^hQE5IO&~cXSpsR) z4gXo9fkp1Ic0cj5VNVDDN5uU9KHs?YbWjLSMMr5<7mTJ2lyyOi)a=-6& z`tb6f$l{+4T*Wlc0ndR>51>2%|Me}0WkdY_z}0e=rz9D$M1qdli@@KoS=((^=1%X> zV82Qm=q8B1M-IG1t>4z48Gx6+K@#YnbeR^e`a#J5Nc+e)EE?PvF<;)l*u3Xbsr#}o zxH$G_WYC(o?nAw^mg`z~1NV_C#W0ELEWcjbq?k4DnaDR@5obdO|LyG2H+tc-r?pdw zy2GC1lVp@yxuAg*c!{Yo8*`g7;&}V;XmEsse}D3D~hoaES~zS2?a#x z9S9?9+2{7`4Qr+%8TqzFjudSkb<(tPww*IZd%^qio6X-U^Y=PT!|E##TVLtu{p1;q zZkf+Nlu@i!)=DFOu(1sbZ}r`YYWu^P<_pPP0%_l@EvZkO2_Ro|_e%!zG_&%YJOs$z zVM34(+Noil=;;5ohKTB9gI!~w_XI4HLXFXaoFv}iKo(KjU%;+0(J=#;siDT`L9tPt ziZIXL3iIeeSrYG_LNZa?btvud%>tKMmx$Tt4|&Yj*+$pGw>Z6+V#Wbfm`_wTp-V6)9Y`W2i95I6$* zYD#NVF>dN-1Xi4j*%mI2D%4EIKxGo$7~L~A9Vh_kVczjaDk7kkC% zY3*R0?yzV{&EmT(RYT{BQOgng^z9u-ZW(ot57gd`LLawxV!(>PB=p6Ch0eK~`MB$g zg-WpXlJgJtu0W~8PFC8hNL;c4x=zeF|B|g+`JLKZor=q`61hQavx#CLQ_t(BpIh{( z|KwehlNVz5od~S()aQNc_f{(-6hQ=ny^&Wgi!HjZZoV?N7FDKbd02y8BJnY8(vvwi zW(rszW*9zdPE^2idRX71umsdhJ3YrYqEqF@?VjZ(skFxh?>C-{iBFYBZJfCt^jUO+ zA0*w_b4lhmaF246nc(5!Iu^}*i9}}T2A0fkRG{KOth&X6bk1k$`Bk}o|KLAO*J{8# z2H-+wWw`IH$E!8bR%3w@mSI4?H=Zjg6F5r$uh?V|(eqchqI%yJ1G+_VSt^>b(dhup zMu6k6u+P}H0N#it(v|QmtFPsXpYRAR9w_x*RrAoiH)4~uV(_fX&`3;Cny-spLl*h^ zp37}~%C~CHvj1I8@%)LN(&U#a3en@BH*~nw_g1Q|M^}7Xv;M~oiBx;;R~h7D5<)kV z)N@4vYl|;q1x^zw9ZE8gdxq-$^;sH*guYugWlye@;(^QK=77d5pNECmNqE?Ho()+* zi86+6g>G5%{94Eb%{QIk=wwgZs@3~fj%V^Cdgn*YkfcvI#*a1Ia*fKOu1bODQq4^V_tM7oPt7#tAqC{{JCF<3JAfiRoi|B%g-h=2Z zx@cER5WROI(R;7KqjwVJ>O{Fp)adn|@5u9h@BjaOzhlmxyE{8OTh6h&Gdtt7FjOBM zji~n>KcOCD)gMP<=Iz_iRX5ZTo}8t_E|rFcg^#(XbP9r5xcXG@s4}3 zs`n_f)T5`zm7&BZ*4W->o}I3p3BGLxb8C`ZY6-d6>Ng%OLoo< zo?uhPiuO027@yv#E!i7~sV!M$Qz8kbde|zml_QQE;(OQ*%z5U%DKW@?SoZ8@<2EG! z_TBwC&!EVYaJ41bbV}rDVhilbc2X!+)~D6?-(S z|AL#F0P`Q>s3L8j8&S4#ip)51S)T{xy%~Z#Hd_a~Db_yb;D#?6 z=ZYp}KW3jemx#dktmb)48!ax^Zj6 z&Yeu^s4KWqdGvOrU|vwi;=_qTO#_UtC3$bQjr?GPM`g^#{#(Lkije}=9G<=w4C^3F3l(Hi^_dCnR>ArG11tec-u>OEGn4QKb1b2wq;$-A>()@L$6w6 zxq7m3pjug{^YfKsQ&B@%;&JVgSJS(Aa;@a~cbYYIds5DPUQ$MInS+y+Z&{;&=j(%`)iR>1#S^bf%Sg0wTcj>Ttll9YPER_P``P4t&^8`n0H z^TV%lo_a|ca^FL~Mf;ca*F!H8=S92dJoU7%_KqKLyY4Uv18=E?81z-kuVd|9cPWmx zl;yzl=6N0Y|=7wyyzV?7;NC5MalU4b*xKO-MX^4(S=uw|f(Cj7Za1mv|@) z=^>Qvz*IYtcqk6(8RM8IMfWuKW<+NqUinVPLyUp@<{1f(KNN?FIJjYpIqr}=6M7#A z5fSRaG`Y}b9Y>R-iQ)?r75(}c{bv(gqqzfYlTLeF0&{3EYy1p~dYyMoW_?!uoS*A-9erh34rm zbLSHZjR9K%7X$JYE%+H417QR+L?RRmB848TgT_E95eoW(LJ#&tV}SYuu@a+8$@@b{ zp;-5D)l8W0ECEp}y!D4Xf(B#bR-sG1_lM9zgK=>GpiBAtLl~gJxVStRQZX0U(s!Pq zc;a55GrWf7jl-!Dn>Ui;pWSJx4&O|lz?pWVhK1tgUL2K3VENhJZXSmA<6nbQU2lO~ zW278?vc`8yprOqwXs06?wX@*lTzfS-&P!q0aVFe0H-23%jIhi^PqH*XZ}ZM{i}W@K zxOO#;VI9+S98xsC?bFsPwRQ0&NJ8p4q{Oe@j~uAeS{k`tu!LZX4B5Q zI#G**Rq8_S?0tmDnM@jycr$J1UHPcR{wh)-cZNQ~^%-9pk!Z7Nr`;D(i@jA7FWevX z5w6V`(ujncX*=z*MJ;w$#lLVT?ju~C5u*_aF`ItBOBuD;S*8BM9j}jYWrmhUB*;wr z{Vs0Q;y+cSFWk}l2!GAoQj5UNrX5A;-wU1JpfW3tj{17v4%f?^U(fa`oMg&bS$Z7p z!_wLk6h*wQ4YA0Qvht+NaZ-JK6y%_V9iy>Z=vXq{yg$s#kM+oNWE z-~DcL>}sD+&!iSZOZgK7wOB9&K`jmpK~ReaLlD#wzz_tr#7v5xAgCpQVNz`2&}jJ9 zko-dn99e!4G(d0#Az_1o^Jf}}o$e?o?dblty~)GCLmX#sHh}na_5<Pc;(|D_V)k6!uG>a{voWxDO^!fC;=aA7brran2Y4;H5aFf)w~O z;+aEM-)0Och`|nGXn|R4!K@!laAZlk2qWHoqNP+I07+1T2tq#i&{-4aI}tvmLf}kT z>;s<9Tx&W}{xQrnF)YwrJe+kr+^|mcR1B$N{}>J^Gd}JthE%hE47Y6DGw8dB*xU!W zLT1bgm{JJ;nCCPxf^BB;-7EgX=nQTzpdJ$PbU5tHcZ#Eg{q#%<&^}q_pa>RFe*Uu8 zv_rd}5i*V&EQ-MpE78vkc{31|v_5{s`eO)`q^F=Zs()Aj(|1x9!Dv_`FqW~>pOKM} z8k^*uciqmjLLmw`?gPGVw{WCP*8L|-g1uYFp%z{)*e3!lRrd`06gt!xa)pbm<&p7Z ziR`IO@{SuqF+BAvKQ59i?k2EtdtBlM?>WylJw4o^Ca#9#@okn=`Rw1%B%L%dc{mVJ`F}O}nbG=>7U$ywV!zz*T>AxEV%I*}9j@y~O`L&@`T@#vs1I<%- zBCAY@&S*N2X{4b68(}nD3kK_ zetXAbCaMj_WRvstZLfwsS_^UIG4m;9wulf+>WTP6qA*O^ZcNVC%XCRYp*2jI%?zRx zRj2SQDT29M`b~Q;o`WdMBY@b`<`faWQr`A%`6|(yX<+ z%4yoyxNh$NpF;Y&^>PnUXRo}A#q4s$EI)}QV;k4(rE2!VU2cuHh33xbeGbYah0@v-Eb(%Ie0oUzpRyBa_8GHnF;PwM3GdY7R zgD02Tmyh5ZSE|$5fXh4O{bc*G(m*6VTor|*f#6>xe+kacxQ*arLUQo;B^5v5i3!;vqie3jcoV4TcOt%a0)1O^ zWC7VCzZ4IE=MtIbEumbNC7@>ueOJ(<)Qy2>B>p*DOYQRYjokGnVI$r1pe_L%qx~HR z@mTOI!|CBS@) zbw~`bLDf6#W4fH42R!ngApEd7nCs+~UQ({AN*3q?GtUsRXtDRoL25t#M3(@1!47H0S z`;E`u$YH$b`Ra||%{80G_BFj_lQr|Oje+f~l1+rc#(-_o*G<~2l>r!8!PwICZuLVa zKHpI$ldLAk?!n`It6%Isg+-#UniBgC`@Eyh9Ibio15Ne#Hk(UhrV739n%#~Llui!w zp+J*~bRI3!)4*(M&i3zRBWJeO`qb~~aF^2O7gGNIh1xrOX^+V5kl1<#(R&a~hnX`V zvBe9Sdk`#+ne$U(iyxv#5NyO3XMveBACbE#u_XwhA`Fhg%t1C zY>7aqh=ND?>=kwbet1LqvqSVpq;Fqd2mF9T_t7O5aQqu7(41gst66B>zUZOen2>Xv zQT8pmKk7zKVoPk0Qt8vERnNSk7ze|ymfm0)X}3otntz`D^Y;^a4cqwBehWveUzDas z4G+;_+-L!?=U1SfPxs*6zERq3_Ta<@mBx2RLI4==@cZ`|3!QhD7HS5sA`kdZdv|W- zR)ve^_2RaAF?}J=m;7q|qRZda=%^!?&+TVNQNjpK#fI_Z$bE(o4OrGkmov>8uaTpp zRg4Ava7}zcq8m?@NS4Cy3+n6B{s(ZuKeyajZ7dqY1v&bLxx@3##;XU;U9-zbOQWLa z=MGyQYjpQ-{UR=CoqnCKOCrD1`qbntC&&6(o-v+y^Nk;j8U_2oWt*{gCAWm1jn};; zl9~?c`|hgjIjl6@^CppEL;I{5+?(0}UAurARiGKP(t*!n+)O2(F942nAT(try8@sj zr88RsDmHH0k4QpDxi-syl-mWC3gAU~l#kFf{K1`ivmcU=tJ_(Mu<5+wO#zdn*XAQ) zJ&Lho=dChFMZ>x&=NYbO#cJCqnkDB@avr^LCR^p?ud8v$!uSdWo;S8a8@JYaNH^vd z?t`NV;kec%1BP?D-xF7*Z1MJn9=5&*qU`4_+tfwC;WQEj+m9pp6fsE}VWwvqB!cXw zIEcs2xC+Ut^{VGTTIs*OWATJNcts*@qZVS=x0+M{t6+ z2exlf8qiG`Tm0<;nJ#Ah=N&yGOwyh~`FleF9AsQfWKVG_KT>4H-^3o=){v^{rYukY3m&L zfaW_1!wXNAH}4M>yMIyR#s&pxGvY?ahA~G-KO{5=uiM04G`nG1gapCE|8> zaKwg!SR^?ZSMw}{XSwhpW7}&iC?ev7?1NJ9>55;m*0_oA4hVV5w)e>lSFEu_sIo0dqor*t7 z->y~&)t!zp#OcMo~k;`Q~#P<*>eE4NfBL{AMw3YfTi5o2~?c>XHW&owf zm>g@1xBcG!zOSXRN46W+LcRWc)AiGRi#-n|Wl;LQfw(v`ZYI6Tu3CC?Z0}*(5`^ep zGuDYOZnseOatv|%Wl5xqBX%3?cGyzU40?r5cH4@lUg+ZGD;qRyXbFi%g?mfOrg^`! z`y}$={a5cv<(G@>hEfacu)%$vNXM$E%R}t*nFA5MzScwf^NUt=1fCw|PWNV%9xK<) zsPN^|dFk)Bia9z3|yPa zaHtoWB(XcOG^y;@`w&+XLCFLVYC)I;;RJ+-t1EOrL%}Rzd8u374uNbvUFcuQTJXM- zx8QySt$>(ONb;L87gvQtn|Vl((z%&qn_uX_WO@*wATWTy2m%ub%*#=sNxP3j({>p{ zvm{<8fJ{8ZEv^Q$@{*X(%0TQNg*Ai8Z6I`l&QADIq&2tCNZ?$Y;X-(+%STM+ z?#X*h2rK7WOnqG(5f-d|eqEovbTe_L7rrRC!)1IvttY0h&5?P$^jI#^E>{MpV33CxW>tl1=y&3xQgkwQPiRT><935{ zNbFYuFN+G6BoFG`!YAhq^$%(sGT#jee?2$6`HFCyY4HLk>-2nPCcLC>M7dU4UOJm@ zQL#IXQ9Hl+VZd&E%yjgR8ELL0(f52J)E5crHy1JDCm)^WD)BtF28*-i4zsqeLT|L@ z`pFf%oIlGbi7)D^hM_c+ghVCiCW;NHYRck8>vv=L_hThK_zX~w2lwj=v2@qK&mfI#LSuCFNKq(K3Atc0rr3lO+`e==u)bsgrts6Y z^d$fI`Hl`G&Mll)Y#oaaPTi*vEga)k{7L>wFPGbZv>?{wyyTBZ2#DH1YaF?H!wT;B*z z#2p=Y5}OgP$tcWPBJHlC6?PaQ?Ip`7&RQnzr=q8T_QINl_*ZUXuz$Q32__f;0VCAz zA_Bk&954b3M#SDl1d?iz;LDOcP|;D~r&Q_=WQP!e$r5)2R>0(790c>@jM}&_hsrV1 zAHmGzbe_o2O)=;*FtY#sxS|wO9D@FWCa?>SBaDyY)TS6m%bu>A}s#3FMAWI?Jk&f{};)VFx!4xKv{SBK;= zhQTIy#)QYiTE^U)QCm(yD#|dJQTr4EhNBs^DkNYyF0W&&QF3Wpq332}+>zW(~Jw-+(Ke&0J&;oT)bZo*AN{@~Khjwr0_Ue>G)3UxBOVY*7BZsj_c5Hkz4~ z6#Lv|RC5f@wBDaGxYKT#zbVAaJA4YBX}DYT!biR-p1|KCYKJpQVgMBAFz_ew%H|oI zKJEE6qV)Ult|B94BWrhu>k0-DXdWcuvDrA2El0M+%{Qphs0Y%MReCKIDfPO1Jfah*uUGfdE z08fBU0JBm0F0AKcL!)gs4gSk>fM(m<+c6!X=L;GCo+aaJQRumT{PfCwGgVU=L1FKF zr~i36S}oC#fcBq-54sOVyxD#pJk7Vj zY97l@`xy*)8{jYPgqGtRI7v@qh3;T<*P*Xop*clTZVRJ3mH9UsNf_`!%c&vLgrPe) z-2zyv!mEuW)4cvDXzzFBlr@;kjU~AFUn7E{lvI?)(tpc0SiacP>;%ur0%~{1Q|M^v zK>2g|>IJw*yHv-Oh=9XA55IVBqf{fFlOovdH_55@yhgQGS(nVa;gwz9?z-zoM+M%d z5mhpMltHo&zXv)27cSpIm9Co-`%Et`J)_~65P%Sh;*z-W_P-G1FX`ZWF90j!314?$KJ}Xz;po~th2`EWC0Co&&RB_<2=`BysuY&Imu)a;y zi!Y7#xJ*9vc2JX=j@E8**e?v`+R7U|$6{OAHj511{igWNl!yt`rc`u&uf+6NO54;n zk=kiw`Lx&9seeoBs8y`1qXo_gz20UCUj*Tr%dbegZQ(~I zn2=r-4*J5_y1eVvH{<~2L=O4p`6Dzq`?RZAsl*2qj-l2af^E6-=CR~wHa(3%J; z;n)^wDUQ+$A?s7ua=W5%Warazyqh;QaOZU5T zYWay~(_SzoeQPu2+P79C*pib5eLyp5CT^*+MEt=X(iAiEe9hS5h zx|+JEN=`Gz*LofG!Z!axp0;SUOmsC{iAidR0FLw`x|+AdBt6p<6yk;}&4r;BEiuUq zal@0g$56|bm}J{wn_`F5-Is1Xx`?+S4{bKjoxAtd?@`gGfI>6bVsq&4N19e1Kby-XREm zfrEA6|BDWCW5!H~(eYAtl@a3ej#&kxLkZf5hXo7x#Rd_*7g+L-?Z=Amr)JAQYura6 z6!?X+K!LBC^-Z<{#P=U5^YCWFdW@QV{YuF`r@C#SWrhIR%QCLkjZ!mT9fC^H^#_NGMF@fY_t^W_ zA;jwn9*FtsnL^-VPkd)iKN5Rr+(&Tu(OZ63<}0emy?6X7_y2(N$sdjEx#blK^N>30 zJ+n58so%vTZ3OS1!(3n4SM3zmAZ-sy%CbI;&3bk56(>0Usnd0ho^zipXmg(sd?YB> zzHJM8|M6$epKooyRD@*T&r*-Z_^Zjv4=|0Y$eBit_9$f-o-vo??9h&ur<+Yy-V4e& z5>uW$JaILaK4TZ)-o|By4^dvu= zoR*)ktW!J`eD*z=FV)iR(Kc_FQh z^R~xm!WQdfLf5IUnV!91@AnU#g7tRac=lJs+WxS>c^-56EeTe==t@awo)i3V`}!sA zQN~2uyHCO_u8A%-R}cY#!hceeDD(4asW)E{Ief`K*xh!Wyi#!!of;In4R8G&p}FY$ z=pg%<#YV%Sxx<{R}0{A1{hrBcY>j96G{}CVEHTUWN~uTkK_Eu1wk%H{nt(>ePFD8C-WGO@^qh zQPmkT6%WzCwY{vWOksZ1si$RktTN6}iu8J^Gqk2+!e#iWGRdf*GD-b`30Jn;ecPtj zWQe1?2zO1LAs;Zj{n*1?M2v8*2J;Jb>Yb61$LGrF47r2(hB7+!<}^&WEMI)Avn#o8 zD-=A&pwy}NN$6wUjTBgV0LT_?r)iu9p6z8Ym|eG1&w&EWuB0;*31;{9>C{8@KoCbC zvMZAwfe|IXoqB`WFo68trOmD|pA`r%Vkf(zsKJ?gPC^(oP)jrk*`n z16W3Z=rFsGz3maU3&~;2QFfur8oWn* z%BjSx82mQgWe2zF=rnVHs9je%iz#q+cM22c?F2R8?oRE1DA61_z_$V+=ZSoC^UYjM z0)Sf+Fyn{>`Zh-T2TN`KmxQF^>Y#tv^~J<^MdBYA0uU+zZb7+A&EBK`nJcQ@yMiIa znYgF3^v!R8y31_vC@#zxU`aytX8<4Wf%Q9Ta3Fy=twV8}E7UguY2vUh?+GOy{v{mT`x~j{6 zU&t*pwACvJS}4_nm+5ZGA}p5ACeq<^Mz&Sg7Olt~lwOIY?RJ@!Mrkm6~Ke@k9W9Hv(RlS+}xpAxTRF%RpU9lsm zd;MZ(aJJJ)k*(Hxhf82L6Hh`zbH0yzm3P&B#Ft)xOklweFiuk<=xAYKEu55pny6wSYp$4@)|6whFKpqECt!hZG3bFB>*!`7b@5-#_+aVB z_e*})jF-32y0)_e?N~{Ub z4lb`MTd5Nd?~|Uc9B`>O8&MV>)E}yQpI1!S6UQ4joa>1EgK+uvz3 zRD;YEL4(|Rtldv|ZRI(SpV^K}ark(JUsT(2{)Qr8%X)&Dl&DQ7Qm+Pke?>I-<#gyI z$gC!TA>88k2=V3~51(-Z!ReAUJvpq4T))F18b7?D$1ue#+L7ELd^y%em2ILk=}?9K zx}T5C?%PJBCP_`&j$Fa1R@^S~bifybY-pGM#0CJN8QC{W+Kpi zZam7czF@j`etep~UbR2VEWL4y^g0)_sD415V!q(rNtH6~O{4GJ4K!UJ&!ZvEqc!oV zrhh|52D?L}#kLcExo%on^Y&JisMz3%PNJVazV_48Xw&nyUAlq&6r*p{VuuRPFOW*X z?YNH(_i6Vo7dB6ijTq)oH6<+_?0lGlhhceQdxEN*dlfkBzkWbmtvC<2YIF|+a3vs; zxQq}S1J^miz^iYUB{J*fimMsB}hM3f^;(TS_eW{V+TlsB_!n^2FqJ72Mjz6el00a z7OW%LPZs=EQl326DojY!fx&<={weMsl2ji_T*}~J$xzDR2ua*W!3mO~mqz8x;3!6O zjH^9%vicK%ch#H|Mt5Otdt+o^@uU|pz2UyQsjdNy-QDYdzJXGuz9weW0WaztANQo) z9_3MmQq741j#X^<*v~(3MF7)+73@gE^G(5Q^d!5nN25>hs+^}#CUz9(0U zFj6-<)F${dVZK~7rP*G&QVp-Lo0=&Nr}^bAPrx~0Kv;ZCJ^%43cSD==Ynz%&oXglQ z`WizEqO!%l3dN3^G21^7{~LV8vO3^rSN%FZe0@9Oct z8xU4JOWC^`jcYM_R%N(ku6JI6Q%oFhX(NWfE)m0dY!!Qa^81^8i*@U`VxgK_Yu(n; za`8{N#`qPbV2Q6vUBdMgY{1=+WAL`ZJ`S7HZ7`Zvf7D&`ygvvrX4+C{yUq|UTV*)G z$z`$e%B}F+#$LxKkV@aAoSXuo9N7|+-y?XewdQd$K{5zBHe0g(7AfkhMk`QJk&&Vq zyJuRT6t5*TTW(G}%j+G@-|MNVqF~hJ&JsOTB(|wgG+l0GEm<;EYbIBBlIAjXa!Or` zH*5X7TSlo?0q3eHzCgT4dBEzM3N1~6a=Nj0;zE`2mQgXpVY;u!Cyeo|@Y~1FgA?7i zwbHTr0*?7%J4&QKd1W$%{|JOTykd=OZ_D&}5hd=|aGLpJQ}V=Hd^j@k@|obP)vkq6 zUiu9$1)r21_G1%=!oiRxIv<^()j%T0bQ>Q}eJx~{K5eUP#*f3Kp8NDm`>!t#>$8SK z8cMbVOhbqq8}huW1C4Bi9r9rO_W3 zhxN&WAq{`1=$Ecad^|-A!EC?56vBe((3j%h_ZW{?_81buawlxNS?HINk}nU>pslhI zM=N`MAayS*AJ6M2t+K=5Z~ibMM-Py_7738OmfnyCbq(?Fncx1b>_I>Rf&(CR-PNvn zBrixk>Eh5m3FP=Om?zm9OnHs$nok9pzy_H}|MF+0%m|Ed03(vX$|2H?x^TdHE39?R zkATe8gLOK~?G9-mGZ+89C|$8~U;Mk_Q?MYqR3b-91&{?t|5`-t!-a?qnY^g+hrO&wto9 zg2A*r*sca(@`n>D2e2knAQ`$TuqNeT3sUooe^*w&Jai8OYkmvX-1iMw(sK}{7cA*| z`7j0NTKR3Tt!%034QnsFArg3CdZTTiSCPf=6o_co2TspU+iz~(-zHN6WOEl48vB{< zL@wDw^5xW^PkQgK*CpQBskwere`q(`e#nkbFABamZ&-xVvWYD?S1zgk?%?AVAwu#e z62hyxoV$M-i(MtHizb1ZPbk0A9-a@MTuqmhMjf%u3t9UdYka7EjD+*HxC*GFVn;=5 zhn?%Y-<#Bl0GZYHdlzTXec**H#S-ov4kBigc`FO8@)^)daHOE%NS0Ab0n4cO#I$dy?;i&VI@b%C$2a=aH|M+@@rn zFLZqsYp)}m-~YUP{irM)x%@q-`^wQ2sCaPzTzoV)jZ+?#Ixm@l9-AkI@7!T_uujgK3?Hs2hnuxuuhzeihVeEa*O-=k~cxu7<>`NwAP_aPCO zWj=g_1`e4{g{D&xFL8F>(X<`f_Qpn(Jd*RE>3?yixA$eu7LMmEijw;g_Ca5)eLEH6 z%DV@Mwx4d3yV~?8kri-!z{#`*Ha_#st~}bL60KQX2q1s7jEpe)<170y;<(Sdyzy{6 z`Vq`0cI$HOV)4-^nV2Z|nJ@PmYsu<|fdsOXq{`wdmwjeUsy|8J{!AYC1>62e?VC_}-xB%NK(JbAXNM?Gn zSlTtN7S0n#0!WKIsalKn3)`%MG%KU)V%rhH z#GiVl0|>pRVg(<@#B%G>+LVtK3=7z+4y-;&;+QWd2vi%lM4JOf$M4KqwUKDcu0p?l z^9vP@8gOk{MJ!w?rXe=sA>dT(wzl5!!OuD01zCK*;vj^dq~Qqv_bP;mmu-{T<{LeE zJK84ocs@O=e$6JAqB1=l(0lR>Q33M(xhnE7)41PIwe6{g{$ADk?%yVZPmI@yC$rwXf1Pdxf0s}t1Z2rYl` zRU~L@TpCd(>1q#Iq?V?@`8gu#>IPY)l}^U_xhLuB3t6O>#`VX=gyf(x(nthjg;qkV zf1nZ5L8Y*yQ~YtsAS)P*@2otcKN5ugU=XM|L?@Q26^ZHOW_igLfXfPDxW`x;%2_KB zOcDE$GV~cHu?@77K)OEwR|JxS!+0YR3=MsTMf@4sNh~cNh^qw2Q9us$-n`)j7hum= zs$b`=aN%o181NbGB!i!Z9^NDFgmzL&vjyQ=LKu{r-xMm{@ITp|y=FXn>W-liw0;|5evz%BhzDL|ga|D2FR5*NV#dxdY@ z2jn7MOl~$kZt0C*f-V-iUqvV=yN-N#a`$id?&_HbOq>*)p6Z+KiJt-ND94LzR6hd) zUJ>bCaUTK0UAP*+{hW*6EhV}H#sr+gVS0f2Q09Zdbq?m@^ z|F>i8BI3vTHx3^N*Mgt~AASXVeQzCMz=XB|pbgpMep>1r@4%U^&v^bp zAyMqr(7-@P&pAbA+97pZ_03%4}%jNP%^uzPZWlvkw zIq5NsV($iq7_NTqmIhA)9KVwe0oW`vUvRHOZ@BNWd?kB}@-RXF)ppdb160Xx0*&(T z_HcMm?SEAI0lORS`)DB1J5;NCseo^|4odLzjd8*;+?cew{s6Nd5M08$To#;xTF{gI z`}LIJ_~+)J^8^m>17vR7L4xswG<9&uCD1Ffa?$QsDSyZ1|0Q_)Sr0{V3NU5@=jIHy z*JA!R@U_es;5P6!*hd;gaYymz{b#^6McobeZ$L&Ea2$|EoWarVs4)Q7IN<|*|3%{k zTAa)Ct_K0r+jfv3td_}^nqK1HiuAr^ppOM)?kLs<4g=^iTma7BdH}#|OdTfOf6&l7 z(51xx%U%kA3D);qT^hwbcu7CR@68cF1O=8oS=WJoeo6cfL5yKQ5+q3d7Y|(< z?FfkUkU|mNCnPx{{-1VPZZ1T^c>u>)aF13$rcNLBpZe06Jf$)28|J!QM0E*vN+#dbF>Qx2UYQ|Mewy@AY zf4T3wFn);k{~h;3+_{e^nvN16;Rr0@G7#rMEb0060uv0O0u3Px!ZY{~a&?3sF!z zrjB0v_TTYx&pZ4-uL;;r=o>(%Auz?Z0SA(i0F<=)EU55r@4|t%j%xo7zJFOXZro|7 z0u50E)i=vRB)Zq2GIH8EI7}lumOB6UaKZPu)&7-D&!w*)pt*xj21yv7Gx#~2W}YaJYHZ?JRzTV=3wz$$~CgJJ|IkT;HMv30=ZRmF#8 zyh|9{j6NsYf8Tol%l7xv@Ya_oiXGtL_6^yvpUz5RnO*?>{&n5mobB$idU^&wh_Ooc>s`KFhIrcE zTvI=Ex>XIR^)6g8gd4`e_GoFH9f9_pF~{Po+e>V^2c|iy|F^Kt5rBEUa|mdN1|qP( zaZ>|i-J@ZY0N&p(A%!Kt%97iY7ZItz$%`U7B*J}&qWJe3nL^P&d+KCBJP(`;V*mTm zruYo>+<(xM|1nxQEKJFD&+;Y`xQ+B{$OJ}s{a>KeAq0I+`_Indv*0u>ylJeYXxAi` z#OC%Z@N_$Tc{!#Yn@gi1`^sqX&zXSrW)k*z`K+pDFHVP@bl?UlHa@b_M0;Ug1~?ZP$qIB4czYE4P7Fv@Z~m((|3 zez}ysJnI1^qi;)LxL2_k&bDi@y0H3(Xs`Nhk>9g5q%4c;mNd;JXQkEcpi;bN7k9~E z6Kq(MTHK=i_S(WFb2Dz(ZQ4@gcVu>kCfqx!Dc}+*e@l9hM*XQhd&=sNQO@l4OMeIs z+s5NJ*Zuj(r!Ug{e1%J9&s%7+Qukwxj57rV;Wd(4;n8a}T$WAdAGmQ{YN?Jaa>tmt zL&nY3lX8YcLmcOGd+mjNX;E|+XY$CoCf zmB+G0L#f7`EAzJUhSS`x?$_-@Z_X`;gqe&=^UqS6{u$>x6%=rOFSq&aC(e{n*h&%` z5IwcE&&AnV(pG$Ct=loaxoK_FZeuj+XWUbG3uoV?seCN$yIX=k8c>mzCu!?0grmC6 zMv!`4J|Vhg!fr9BGW5eeFV7@sH)*EXGOy9yg|ka3Sz~P{Pj6K zGnm1W5;|DH3KBC|!D^wyPoTpXoll_;FgtmnuP{5GLnB^#Zww1VhhOR@%;E$GSdB&M z;s%FF%-{ych7K!2ho#@SW0~*Am(+m-<4Mjyf{EEjQ^+J!h=Xav zhGU?d3h(l)=qTnn|s z?`(vY;&*kX=>5Wx3-LYiY zIv1=e1#yFdDqrBn2URv{+_>&{_-*bDq11+O+qF<-H@-fY;6yb+t5*y7T%+H8VH^QC zQUl>+DW8?Y>5_S$I7oqe_Xn>50{#o?*8pvDoL$&4o+U#XzpKGlr8VP8_}x_UBM`2; zLmW@e$Jy2pz^@`V6bL_teO7ikZGxX~!p)#jKp5zO z{%>v>7sLNV0}^o93jE*er@^e0EHiWOW#bYMAZzA#L&UNbdGFDGrnle(cN|~jCNeFhvyEkXLoj~tctIF?7dqxJMyoRV7mksJIYtq`-)xbB>A#s6>O2- zKDJ(s2(>(gn1tE&Bka6w)!Eb$!l!DY@N3bDof_5Mk@NQ0+10w9vm|;QzSeBdg{9wl zv8m!OLuSd`@&|tKOzzvNJiDx$ueeBWPK`hcv!j#oz6!`YpAE`Y%>QC9oOjwpo@&2z zrSjpO?Z@GIRY&c)vI`zQ&vnJTRyU7)_TNh0A;bsf?kC>!bmPdLVHM4+nh+|jbSZA# zU6aJkv`#51g}ELWd)s*Iyjhk)#k9HR*42fRTq@;T51wv@l{i0 zTU+!LGQRilUtKmSj{hU5JV3hVt~77?$_#tBs(@cNOQ>GCSgdZcbl9oxdZsUvD)IlY z^&e1CG*8?x3X&E9SwKJ#VL^g|WDv)*lt+Y*_`n@rD_R$b9*g`(H398t^pB{pbrbfQ^#0m-m7EWWUE6qjn ztL5V8Oy&3Dm&1F^%gy_0uIE~Jev3xWs|SB@>J!~eMeWnRUxQDXFz=I#;PVP^NpwOH zFYY`|*cg38p=$Xo52KSD@%s*m5$rvoOqwr({|f7EGl@>WFG3hv0Uo<8(FsGmz$OWU zEs@A5`5_dc75EHdlATWwFK|eTVN3D7?6vc)KVU$JeLi49=zKoh*?q{%>GJ_=m3s{j8u`_W zWep!%hVhpGI*9R?2)c*ymzco`0*UhZfDg&^`EVCf?gJj=jr93I2pRJEK(zXC?ICm! z^Y0@DCt?TyP4<^?hXjO4TtEWiB;4@>QY9|%19BwX?*HH19&Cf<6OC|kI}?%zYk9!QGOquLNfh6utLiHKCnTM zejlDehDa-7N8xX3Y3-)>{jh%LaMzp3Y-d*c@%Njs-tNv(!u=Jw$W1miUw0@%nF0QWTNda$fS#^N@`&Ie=yp7H&xYbbr zh*d2ni>*7K?ugP4q+KtY>zx4yMVAt1`w9NS&5wf5#CJhkB2&+QwnWd7LuFOY^|~Cf zUa>d1Ylt!F`LQ9*J@2!UC??cSa;Ds8?LzGJ!BdWiA zk@8bW`$=l%E}MvqodW(nk}`RFCWXfJg*k=Qh7r}@FLdQ{tk}%ml8WSaCaRv-9i611 zOuIje^r&+E-k~68cQHG+PtOK^2*lJC1v_AO)=3?2es>638!%qH7vm+(2fufnV2P$V zQyys++?mbxaQZDEd%X7DA>x*T222pvDw;wZ?B&l#cki0QN(>}Cy8O_Xnd|Z4w}67_ zA}hinp|{lly2yoapz0JLTAIZ@jL?XW@Y>ciGg%aQGyS`6sMr>#21n#~dFR&9dq8yu z2ss4T`D5*I@5$cB>U-Wf@Ja)=iyw4dB5#+WflF}z1333FJQ)00VsurMf2HUuf3tpk zvnT;C0p^Tfq@!o)pFcO=iM}Uo64d?wnp)1@q3C!Rn8iFw2+o21HMlsuqx2Ylad{bf zOL0~MZ6unj_ zz;k2NDjNIg-FLUI!*TyxUe93l0Y5wRpWgu2H+0}?=%E*R1=>ix51{op27Df0Oqe__ zW7)tEXF_^n(0ZI>FrV8up5%%8Ag?f}e_ubfwOs60CCMNKZ2|yZ#;t|qVig$q`Ls+I zmjUR3ljn>)i3I*XhN69nL{f1M4sMR0Zw}$-hnb6Zr$#xTHHD|{XEzx|cEZruI6vg!cZ3B*xjsK_f{=EPR_v0n~GpfrWK=$Q0 zJj@)O$zR%kB2HGlNO$2Cz4<9(5};d!g8-MnphMKnKHbhPCk@l>vYF#2uyo;Y*aw7E z;9EJG7*GIiqQLABFbS@3pB%xB*MR1Ab0fIQI^5`B8r;jmT;|(Ru{iWRdm9_H=}he^ zvsd{0{N~I}<=olJ1EOk+eFwQZTKP+A}>7fB00~Ez+4yUiw@l z(fo3;M{Yr%?#avRI~VQ@*JK3*RpS1==r7{u{i3kw~~!hp{i z0QrUkbRGWR@-z%I)a{cCa6F|K{ONL-Ikb$F&8bJ%arMCA9yjNgyQL0kKv%FD@zw?B z!qw2>tP!&11^VBDoMYg2qAIMivI1MtQJEuCKtP4}YBL@~{Xb^ZRZHMqe$A=02wTr-t7v2f9> zWiik4alTIb*C_=@!nSzvPas7kw1Y)e;1ol?%{q;-ooXZbS6T~bJ|Bh4Q;tDPz9Jie~E zetWjhuljt_^Ub{jXbE-Di-B8e(!>;;IzL4cE z<@khB$3vS(d36cGt{l06os%)b{DL`wLHhB+i8Dok{9+uzs1LgL+yKpci*5`L&eu5%Gh}#b?H!(xmRh`yrMcXRo5}IYoczSpZKduTqU(IlRq7T&tDBi%sGGTX zztpH^@FNmMY=wd!e!(k2>3G^^28#@9>CjfMHH)~4?q%aL_XCn8I0XP3{^Lc+un zW%*SngXfnCtsPrFVL5#i|9GN?G-uy3tK!ELRett?%ZN0?LjiWtZ6}X*V!WD=Gl3O( zVev_37yFm?TW$N%o zP9>e;Pf$m;k%F;pJ4(s^}Gkr6ndTspJAey zr+5LbPu?bflGwlt@RP8F1cX{Xd&}pt@*~6jdPxrauF$2@dn)G5{+RHt=(QV>Qr88z zbKZGTeo-6UP6u4F)eQhoy5WH$Xtrcexhn7{sOjrxqGLa=;4}i_vT;L$tJc~LX=ObT z6w_{4rAhU!MdMH5g_5k@&+IY_{5?j4pRz-mg>xI5^MmEdmb;gD?4SKLyl?;8xWiuW zr4DQSxd+7cZ~ei=>gwg@%?l}C3|J)$g9HNe!<3Qmf!u}qVluptz=j}Tm?TeNemF91 z!=$m#EIXfQg}?85a)#s!MJ4_m@ozbJ9x(S{87gqdS`uwN~Xbl6aA%wq>4h&yA1p@f}$yf-9ghCahPJbd?>pTaD8=k zy;zHO^C^#kzO=f486lb*^tL(0I_b1_xgt}{ylb%;t%GtJf9LNH3&5>+z=MT;3 zgF@=s$8={?bn~0#;%752W)I^hP9s^aW>juC^?-rl$=bLa;!Wi4+a6ce_vvWoj^bKg zOgvkQzKO}qVsy;zoEv-tdF_(5d(9cOIRqD-aQ$1Vgd(;XsHMyFR^CsW8gX@8aYm_* z1#NJ?oVFDw-uw!5?c>ju?>^jy7n9ob*{dmJdKFgf9@X$(EuMdxDNA#yLsf8S4sr4g zv@|3(m+KX#rR@*gG`X@R$2pBW-Mn}V;G?od#Bp?En#uj|Q%K_3&6TOfCTB(@orl}l zDixVj5-GSS)v6ap{(Syyf_u6fr@~j#))e|MrT6)B1kt_sIb_p8col~sC0ITi*fNtO z1suPGuqIyW1b=OOvVC8Z((IA{R(6S#n!y(rXi-?D%AM>SpN(fQAwq--R`#;bhKS6h zuVs+>WC)_$zh)S@NtkaqC zvth9#Fk&xmX>_TmwVZL9S*knxRZCA{_p>UuW=iNLyCvf!ytcX`vAJX&_p)?!wYtt5 zEq<9^n%T#en7Z<&Rz1bb{PE?yj+RFa(_2qYv%*^D*X3S+gr)ilPD&GEs#LEJdIn}1 zIa_1JfBt4HHxiMb<$^b)HGK8EvATM8Sw%v&8iA7inOWyhV0Sk(TAmOk<`jGP;&-8n*9#sP56z?e1dn3)gBruVbdn(fg>Z zE?-fnCE=A$pV)}&c^UJ*w`+ZM&$+zSP3%29uu&V<Em(Z>1(U6gg;Zsq0l43C)P8UQRckiQ3w< z7N#htY3OjRYmVwSffm=qXD(57Jau#Xn_9Kudu0MqA6E@r*fQgsxbi$HebDGsNk& zWBfE(fTZNxFTF~4+w)-IciW3#{de2TV1xwil`tiO_F7oDqUj`#u_5#f$M_wT0@wIG z^f|7vDO3yB*c|GLYi!9%%B!g1n3SB-YpdL4Y;Ma?^&H})5tB5X+DoX?_1@gkkZ)}f z&sN1$Hh+n`N+>2dJ+!xfPsZGxm6^AXEP1oApx;*2v^#%^r%H^I=Xe+AS|cXe48zd0 z^<6dtZy$DZZ*v$Tye+JMpEQQbUEPC*gvyUHK9hw(;~@L!EdviOC+j&ggVRCwM;HlJ zKPp!`}ThKGaKJteOM(Wf>72}hfszY zD`r5#ln<&yBx8;hGcRHK0;+RQCJ!rSSHe^TszWTZh82U7FqMGnMECO0BWxiIkNtW> zzrL{2NvkGAv#97Ml&SVGx=QNT6D*A+pl07m>z#4>mm8^lUUs%g9PDPZ@K`cKApt zrRosmZB@m>rk(WS2iwIa4x66r`N;ItV$)q}fgfo#3L$gz*WAY$)5I_L9`d%|xOCO( zT;nOZ?Eq)9z5aGV!uPye1&=-~vkU|$mLWv8Xf4EqP@BCbEFJwla>y7Lp^B1arFS9E z($S($Iv$4Kf={xiJCbR22DumX(SPPj?k1xK$i5Laqf9kGhZe<*?JM&Fc`8N&%-l_>_Ei#w|%39BwU zOisu9&6T<(neXfy>YTovXZQ4%E*yuSB+m?%H2)#7LhSD!C=1eUQl23?kW+)NOsYOb zVi74M?qp+dl zggfSur7+O_ zW@7(1E)qRu_>=m(bKG);Nn91KiqYPKYVQw|e}3yk(uXPOtRV#Yxq0zk-aD;L3GS+k z3NPgvrJ!iL^(Axj<5anf1yVt4Lq)lzvN)ZbF;<~iExTU| zd%MnW%{hmtP3D$g$!Em#*UnJOSr4h}6i&IOj`@|uSoJLM=1eZkaUGM&t-_zbtOvU4dKyrH->>!CWK=9Z5q zqe#@`Hm;D5C#A^Lq&MdN#l}ds@zK1KwHzTLTTW7?dHBvzk(`*K3A{N}H04%`zemv{ zL(%6f^eD`NAqP7xoMfLgBarSI8f@?)@8L=vpMTg1g>q!#Ea z=+8>n|4`HT-5!hw4pIpQuoCq@27x%T6tIr*K_Cr~rCADOp#Xu9wzWlbGWm-ESt*<3_uQ+E$vcQ`eV zLSL5u6$U%APLh0H(B3vDXNu0bi{%81@rVW`2ZEBtzJasA01Bfl9BrF>1$JHzq?daQ zb|V%fi3dqqzg?IPGV(aQ4)kq^>%EaD$6ZrnIDP{1D1tnn6+j+Mkf$8vX^01T%0ZsU zTyQ|oKoXl<5=D?iA0*koB}wePQ3N60F&uM&6A~8E-viRCG!uB>A6_M^`at@ZV^~1aQ8o9@3YJ-((?uW=nqgm^C@&Plx^ zW8$U7Ubvo6zwEM>*#7ZeL(l6Egt-ounhR_J=%bwo;j_hN6RQ&wp~j&ro(LixcwR+T zl8_=zAcb63*xa}dAS>YUJ(JbzSc%lG-dm||W5?(}ud-8cH5e5EJad%3>dfuzz74!- zL^myh_DSpuJj62c92|;AMz;2_%mwUWcTVzb*gk?I0+a zs6Dq~LT)DDeMhwPAT1x^NBYZ%!fpSzphL=3xXWws#J-pM>YeNh+jJ|6&w(K8QqXzs zp9kGq>|thc5d1O|Skyiw;CuB+S`z%WxN-QOyW2cqU=kZ7)2W0|WW9?%=YO&Sd@;Tl zYh+>RQbmNxQ*DJJRsQo9xj@!mi~DuKr4)?y0Xpc1>r1*&Bts-{U#2N|dOs4}`O8;$ z0hnafew}4>`BCFF(;lcju3aZq&XJN=6t+0SI2}azCQ&4=v~}`t_|>P;`z}_?LZg)# z`w9;6jYX}e$Hk+}U*zE|sx@z_!chOMuv~o%{ z8%^-0E7ZGZs8-ilqfuvQo9lf!@W5*JntQbNcweZl7?nO7)y%f*&Mh-jQGqnG2%T?m z8oTE*RnI(HR@Wm4CQ5WPXV=T!+cS`zLD?Fu(=KYRXHX3ldtUFyx84QxXQr;|k7~3N zDv^`SK;WPZC=<^ zlLvZ;6?M^INpRVm*&q5DttuA9yweM&_%~cVh8KNSXEIk11J>0(5t$raiVqchMh`vx z?8s!U3RX7By!|o+To!e%1Nv--m*QSbU?nVm2+(;0W|dL-a&eOx`fD9X%Lh5>USHm1 zVrhZ|Q)&H;APON!*PsK^dBZ?fxni&?ag*N`%(R-u2v(5&!wLbhUV{CoO$4#}E*RAN z$3Z#*)=Z%45lhu`FimVYC_MbHDen3){A^vM9ZCE4=!K>^o7U~^#_@5PxP3cSly1c8_DW&*s5f5AQ#9bAfQJ5`pnflS5V z{W3Y&b1?BYa4ZjAcqaxysa3}y%5RX@*i{U$h)y2@HqF3pOxA++ebZpQF9fhwBW zo*G_QZfY=s*QJ`*dTm+fF&ZH4&p8)qDc7!WukQ-q?*AYOj*&?~&Vw~zoZeuGbUF6D zJ{fQ+0tgRpL>&V``HX~Nhnrk{^sc5#u{O(~v&O9hMHgrgyb@=A%5CpgyaaH7VK&F- zO{X{F{-iL_I0zc`<2>aE`n*)Of#zYWSF;hKc&6U8f68o+tWp~qW}ccU4xsbc1?|r` zE9toHL|sSGtG=J|?6w7_>)_EQGb9J~uZxfPHA-8ycWOAj%gT?nN?yC2KfL;+>ZY|- z?%u!f&eTWV!Op_rcTHa52m<%4+Yp&bySHTS@cIlayk308*(cN~R!QH-_pW>S;*5a&9kk1PrS8ecJKb#b zd2PA|yS~AxNq!u4)ph9l@WO!~YdTRPKyJ>ig>lSock?d9dw{JHtNoU_J3D_q%VOS20B?B8fjO?e76dR>0jI%gre zM@_cq{FnRDbU$$VD_3E4#TG^L*a+MLntU)kLXA~@1)HRtON?(i!%#LLe`#VO?s^Ws zWohNgv#VEmEgs3qzqTc8t%b6J?d)$aA8#+Nx0g>sF~N4;4BK~LjhN}f9gTNxFIefl z9gW!OZ5@p`>Gd6rxMF1U{=JP5g#DfDVxJ8TSR*0A9xHp>XG09u2u0XqXS4ZksK6TU zBkW(A8W6~wGgNZmW~cgYSi>40A~^7}XGhA&wm-od>8#2qjW8(ZqhGUahr${kBRKH0 z^+ zm#NuPfI9mT0Ywi_7a9rr&Rhf8^A#>iA<0S68_T0Z5^8wV5tlE}`}^B{XF$Yz;Gdi3 zKSxbu$M-KU61}=n{lJpk6c81egM|_E^J(EvY5ik?|F#Ynci`~9!22~o#Zfd?BQ{R< ze)lQhCW0=$SUp4%0MGyDAq2eN1BO8(@k=XUG4XE@kn=t^Sf)E%QHkRJ_+jp1ULzg! z)sMul?cN!JKJtuR;SaZGF8=4GIR&SzqM#RHu`kAQ}O$-QhNJyvdj!@>tz@94UUU+R+e| zow2MdYhW>WL=${6%AY^mPZmkK{7A*EkKalD_>E%WtKf#}&HCK%qvVEuz*^_mFU0|~ z#pLai4`OB<>q*8>;RpP3f&K1^UBvmqtSy({G`v0PbB;=0tV@$Mz9?c05G+1(*ejse zTP;~=Y|aVN7PJ_UHC)qeBp94w`ju=0tQZ$ME%`ObY*S$S2|ENcJi|fMa;%JxWQj zuVuu1|0RBc);>;>SDtIt6V7qIhfB-ptxu7usP^`BbW3DX#DMM#tj|-aSbdrwa3=&; zAVR2|s?&Iv$mgkU!gPYoy(6v@#0jo+#gTuebrQ}9f`pWQ7PCLfQ{{Er5u73=eQ2_` z5@(LTd;k5tmg;t@HPKFz+H)%l_rQCR2j`^e2Aq7Z&U!EUMo~6#g71%>C`Q#p^o!!j zzGYaC=M$fq>>0J0vvSg$|CO+@V3d?ou3ve=-DuTS5_-ogg@*}GsZIwp z@PhU71`_*YA5!3cj0+tnTBm807ZjwMain1IRI#9Mj1PXooug`LnK!C^sQFjc~END5$ni5Ti7v0orycqAJ#6g#jxv@*l~kk8N`#8MbAMhq!zm??%71ojO>iV(Jf zAqACT#)oFZcyxTAxDYEJC?3RPSmikx1QMbqWy*j_Pd0;ZB85p$4t9t zzEDbthcEO2B*Yg=1xfaWQbP)Tp${Pq>ap+r9`pS4g+7Y;c`x7qq4WvXi6`X^!-h!t z!DJv(LExz`QlYR;h*TsD8(%6GCL?cJiA}G>5XTI0Buy5Pgxn91kwknsdzz#q2_X;A zlthpR=u1K<0!$?l3GNX+Yypauq)KjYk9|$+ z*e}Ch zZe1oRPZ!@$Hp;wgy5U8;qU&aF_SY@$pDwnS0FA2)$F1g%%PQ8kMb~K2@TttiW^c!a zFXu!e4+!5gGT*v8fI-%}#x?g!fc#EI=Fa*9(m&VrXn|o>lBs_V8n^D~giT$;z$1Mv z%j>3N_~(eaLBQ^W=>9g6V(o%+zmT8`T-s_*ceK-H77|QdzM9QmjH|T^(WrwD%gtAa zD9u|vWCJ(RavKE~KGf^!7BjJp$1A*?R98e%e@i%QC`!0&tkta@b*$OBh8NI^dlx0> z=_|6r|0HP|Z;=rZT=1;km?Ef62_9 zw0%%KId@dAJ6l?%>v_plGl;KRv)8Xyvye|~jlw6Gz~wAWI6>tX5ly69=R^{G>&9PH zBo;_v;KyI2=f6A@E=-)F+O3f~&r z7kraUmP?IGq~@g&US!l%TLdqsE~+r};&1z)LsZXNDCtt9C@I&fuE!~!U#oN8aMhb7 z{d(4n<(n&AX`G*h?Uxy3_^E>c=xy!q1^1$@NB;qN14&Y;FkTSCrUvYQrj9Z5y{ud zuaOKXa-%Y$gIuZDhzLUC--~)GI`RuEcCbaMFV6=R9a83ulduRC;x|QX^{S>8{O*=G63A$6lqU*b0{D$|l5gpA03uZXc32(W8rf-BK&RY+V{R`m6PEu7M$Agt&m7 zf_Q-fS#4wtv-b0t#q6}` z+?JMsjgTgQ+HO;ULQPZNSQ3Zf`-Z>Hhb;Ow#Is%rZ0Z=ChX|j?m0z()++lviM3{&m zn2afAYf+NfI%9m&6vJ zzC%MK85tzOQu$6!x{XX{IEVfq^4gbOvVp4`K#m;%jmn*oqo6tYDea``i>8Q8(=E|!3%I6 zhS4rLd~TAGu#ARM%LL$3Wl6rWj3%Y-48Rr4l2n7;symqJOUImpTrqlf=n@0Z|F^=U z{uYE=ktJzw38kTC48|SFl619%(o%m5#y!lE1Qp(^=H5EC|4ZY&y1qlbSO#T;$ooN= zs7*s~jk6_FETJsBPUB0nbe()5aq_l}%P9wyff$ikI3}2X#Uo=+wqv zKT=(YbH#ItU&8~-tdS#)phlT|L%da3?7c>0GZ2(h!e95cv}T^6q$V+{dQ$MONvUvE z6R1++OHWyv?d=XmgN3{Rs>z@&F`Jyo@=a=%n{S@=BhChK2j$MniiA`A$GXKfwKnC0 z#XFzVr)uBY96OP@u&?dcf=Xp*3l*MUGqL4dtZH4LlFZv?{ouaIsj`*LsSV}()BQJ# z)+JM&)PLA?u*woDa3>1$34}fl&Rm_-7^J$)mPFAAW!W_Cl$6cOJ#_iO+d5tYH%lJ8 ze_mVG5tpee93+-cKy?P%eTwT!vgmnB(iKlf1NkHCf>399Wqrxo&VR2nM_lH4bf$Lw zM;iC~@U|HYw+gqp$G&X~E_@BECae~-j{JTWN?0qK{JCT#bYVu%XnIDD!+1B&>F3OO zk3%Uca=+?qv1VH6T?gJ|50A!G3GJoSFUy9nfqHlZaM0h{){+R2#APkM4KdW$Lb-(p=(j$`@dTP&4p zKbzEqmlQl46PHUQ-5Zav^O8#3s*w{Yo0K)so#t<>?(1`29vT?SrAsSJ{myS7aM*!8 z|82slG|nj1IGu-OKb7t$}#zFZ#)_NBBn5?b-_kH76K7Ntc!M#;S<=L#=rpeAbJ-D;bY^Pv)BjUR)KtsK+2 z4s0|7Z<_5_`F)7NK5*T3<{IqPK?;cKJqn^agOWbKlxaJrZqrqvxBzJ~ zWAp`kuL6n$5qiNn2H{rzD}X%!3nyS_zJj+5;B>atu`%}kV+{IN`1W5RvReE=WU(xo z5u76vaKxeDzW|gxy7Eu5>c0Z%>c4`9Ay!f$*ty&JWZVFu%RuR%6w`mh>DBtk=HVqD z7<*|H_yb2k{?m;JDu()uG0C|v)?V>QjumGPy6Sjo;!LsW z1|DgUcHfJTXT3N?k``PnJVz7X--c7h9|F?no;N^wWYeM;#g}i>o5Uv{Y~zI=MJhLZ zf4lyxqvBHN=0X)2uq~&6&MNv}@MH`S1H{R7F%fOXMkl>(JB1^CX4l}FpciI&U@~x= zAW$>69;geHs~cX+odV3_hRqFVl44d>U0$9p~V3C;jFSvbTaU7vO zz{;64=6UtEJpwSkaPBi#8#O^*y#CsgK|CC2SD=R)21Wh0B&K;_6Yc$X3H6&_F{-+- zM4z;kFxeW8~9#Z%t<`dGfzAy=_cygrpt*$5#X72&EF`=u||k1aN`&k)|J zdhzzf*4i?Tz|FHrDscbgpI^M?B^?ml$w(BTC?Ddee6tQF_Qntg{jSw8C|N?ck^hAe z1p+;}-xxc=$oG9MfdA@x{?pCWJJ6#QOtB%bUojr|I%640aF#oExTsC+0)8-YZ!JZo z6g_uBG_K@+I5v7jhW2VxI*uPgv!E(I6*;>o!Z&qO3htE4aSGK&Obsh3 zj^`Q9_|^k)xSNqv;XMLP+x*L*TL0Tq1~+8$Fnp2q$&25}y456S=KX+7;i_<-=6(ZK zt`?A5c=Zie@z(~n_keq5{>Dc&z=y<{%2MLA%=yydPdU3otNCYXr}#1|BYneMo9=w{ z*HQju|EdfAgGk&1~djHr?}Kqan)z);+3Yk^r7-cYE>ZqrtbY?3la= z7`BcwDttC_0vX?89|ba|kdlN{M`MR|-(*Zn5GZ|rm5amtwtDmq*|YanzW5Y1)os`e zi4;lh0YJ<%ze0?T><2_K{o5lzEWt!Fy}PJTf*5h$pVJM`X0>#xfT^7QeJPg*K~p(S zL-&pIB>n9QXi?;kB4OKVqAAJ2TO<8qno;Js*)8to^hI*KWa`~!&zDX&=0L1?5bIpZ z#hk@3YOqBuT_Or~mx?#bfZTI$Y17#6!@u86O z9@uREzh;{MHTP@}FG;x=2BY^43($1K*T{<^7jVDr-=^C#@E}u#`{^=S>|NuV@&{gE zKpNZ9%{~J!nv-pH`D$Opf>*qa&9+b8t-0M*1&Qa%GZ4YH_S>-k?u>TBkj(_zVDEpa zP+p>=4)GreJmc;*JA_YdBk$O!0{m(vfWGMw9DIRzZVv)Cs2}8W(>qyrs>Re$xT|-_x>AJ05b9UtTrX}U1XsgD$V{Vw+X3dp@|AYP8?md(QCz=A?DS>v;9o!6^!+SNYq#n0-A(NwkgDYd z{a^e(I2D_*B7DqDHC#u)?mC=`5c8MvI=o?T1=|sw_%B!*UU3dT=lC1D{m+Wbd-c%X z2#`(#-Xtv}o4cmQ=hmGO&095pBex{;cD5`sXw|F!emzOiBb6I>waGN}^oSyHQQ=c6 z7uKLs-`6-lxgyJ`;&b0Dwe^CBpYK8(=47>`h4Pff24b1O;wMQwCm8JOIl0(6Zc8b+UYM=IT;cu@r zuLN6E2e}m&>5wY>ZX#o&aBbNuAEI&{+P(``&p)B3gObNn=ESu#Y0{(X7O8R~f5SHg zxIHZs->!fE>N{PLk)H0z?_U+HO=}+F=J`w%{l37gGsJZhzbREm)_Vr#HQr>#9-=rm zDrd8(7dsxe`7_}Wg`$+oSrU3Z?de~Z%AVSVHKo&;B+;@-;lb+4zREFawcS-N2h%)HI+)@@mk?dw`PZq9IhfE3gv!0_9RH0Sn2-oUg*bcJf8z#r2SWx5)q(jya2W_7MO5Fv z{;BcrI-&l7$si$tjgOGXdL&5k40b2zTc01U%pbLEiyv4FJ`&g@2#Gr+N-`8`Fu%~= zI|MQxd?8#=NnD0u32bl`h(+QIyAwr9L?8wx@??NWVlzz0zU4Zad8Nlv9&wUC1yO?j zkgp-3_zLs8FZ0z8q6NhyV6c?L=0N*w<8*t>H8y0u$x2q1!RJ?OkZBEkWS#r+FFa&T20 z)&D+z?dbPW{imJ=5$0#j+=)FG4WmYbt1)%=92JS~^tFuX(>i5e=+i!x1qnCaNar?) z5GKCSr;jq5b4cl19;R<3rR}Q$smh;avn^JL2oJ0t0y_ry`d=ol(7*4WkMOC>^pKPL z>Lg*3JKiTDAO4BshLV+#fXY&wud)r4Y?l`9WX4Z?F`ul-em#5gJh`rg{ zY5W8~FR<=0L`b+46lmf&coVel)OMM0+&pyMYB4a=Hg)vMaB%8qgM*F8nLSF;0_d@6 zz0sJBr^&a`W^K9I`hI1r-Kd#3;ZyP2B-Kra$h^Ak*ig%#iue0uB{hNN@6Ypz6E8G` zXQyYTpfuv$iADvwTFcC>H&PRo)J&f*1B=Y~yN^~WixMV=k5?(1`>Z8iK!&PIy08Ch z%UJ8<)MPJq6gKkCveqGD=4t~ODleRhb+sNRi`RjLMv$$uk{S+}@uO1|t|<-WrB}dw zHI$dgC-8|I_(^@pYt%G6eFi@5FkoJRt{R4C0aC!rhF4cc`mYn*tTvFo_!7#fa%5Lw z0Ga?rdBu5x{IQJ$Lj|tr97#Oh=R}^X(Uy{E z^Qa(?WsuVfnr0pJgpK(oHFMX#0c#-86=xvuLVh5SY()P>h5aMmg^4ihvf|duBk5+7 zHEEXpB>AzU$mFRPmBP!9c$ridxZ_q=2!I2ZYRCM59edw;6Dijzi8(>~CQn!f22>Rj z!6Mn4feTd)h!!Q^j1o`CyU=!SzbvRf03!nlK+kR@`&sBwT3Ys~l%9Gma4hbB_yx5X zl(3RGSUJ)0n=uCTj=!A_qYa>b=}0jjHB`Vmz61Omd|JI~@!w1tZ6KeVeg(oR!#+|N zP+yhjTmsnuSb00i+4_ViItR9N8@69{6H%xsZgf{TppEI<*6oB7ay(fl20W1uyav)+a0FlS%B@gR-2LxAX?VKGc!JFOG9G?->K)lQ1VozPV~oLF z9Y-3&Ou^^ie|T-76AAI+!s2YTt*7D{<%b@vR}SGOR}PIyXSd@A$1{Q+lQ@MVTEsqi z53!*IHk8*i*5TV=hM6#pK`#HsiuH)t4Z%EqGY7&;YYU2~FRRTw6VI6yGWZ!Z214!VEzVWcoOEepEb1~~ zLueyj{_|^h!TtID@tY~(UZ)IqW%hy>9Fz-J1wvDfY8<*Bs&sAPTD5)TN6kO+#!35> zKUx6>*2%R>9Qz;Ygp7WDS*L4yZSa-3mjFo3<@o6Gg;G2|o5Q26grmhRp?PP9%jNP- z#|pUzvZz|z9_AWqd;cS5xom6zlagvir&KkI>fi%aw5>NFpQ+aiNV!OA{65FUp?YalYpq?I$+ z*|PHVZ%gl}S4oU>K>3xXx~%`5J5r0U6M?0^EioXqRE$#CTAF71u$1cGY6kUM}S;P223wwZ#9p>|n6l%-hZxMdS?~Mc(0mK#a2N&!u zF7}wuyNCD$CN$8J$EN*QeqIt>Pzc4HA2P7t5bVdk@0g(d*na5&)s9%QfEyY!&zb@vkkc+izoI)CT! zS*^{V`g8|9*|o@umdl{|RGMlrx+ZE^8KO1OAYRp;=Kh0zj+^fD1CplpB9z>3^4hO# zwh{ej;o3jRbVU^{PvLr!_t8cd;I?!ipgLF;zdF4(nF_wg0PnUtnlfGFeeXrsdr;Mq z{d;NQ{u98_(Vapx_%>c<{(N!;a32zAip36(|}+_plmWH5T5;ZYRWN&~QQ7 zqKq>4fkl(A&)bqI6y-~e=jbi|QXH=#gTVD*QT86TLPxDqs;s$BGz)ccxY5x9EbMk{DWUBW}OX*?u z?6U~?yv620H|HK`$8ZZ2KQnU6_jGGy*t?jXxh!hZH%MrUW}nBi z|9*zhHQuCZotr669vI!Ow~e@Ao*um(e(jdYR{s42WgQ^=%e-2xmZE>O=FbdgLdm@R z<}3xJYg=Q>oe;fKZq-G?=Icn;AMH_%b7>AKA*r3*_FtmspP{&$jA8cFh=+JfYrgIn z5+c|zT5KpW76R{1r!?l$4~)YPKJF3{B8*aJUwz!)N{Dd5XsMyZ_YlF_@q6fG;)DG~ zV0t(V0m$62EQylCb-)0&p2KIm}QFPkCQAKw(L;wofgdGF)c`WrWt(f(UWTRS-NudkgVVkZyj%m zx=1R6pdq@bL4inbI{={k? zSzdn5Q_-c#Q4u@L7BUN!P5*R&>7onJjc;-`!-h5v5`>OBx+`feT5Ow)qpYu2lm(03 zt5Rw_o6>mABT}69OF!E0(D0uZz5M81?97k+L*X(Q^PmQ1=c{{Rm?9!zU8#FwTCG24 z`_H#H{J_TG-T2>O8G|+fCb;@E17;B7--y25yVkH>sm*_XF2Asuj zrL-C(G`Z-DO<}Al(QlqZ$?wVJVyPcWXo^9xG5oM4z~u)Ih9pEv_AU$|+1rbW4d&FB z_krD|e@p-kpf~-7>Gwh+8V_V2q?DAffUDBoDbiBX4T4CAbazR2vwOccKF{<2@_vDtGjk98cITcs=Q`Ip2Ma3; zkD|~Y`k0=P41&dI@dPu_1`%ctgG59{6X2~7@gAc~!JsejuS8ys*Nt)!oQdNv8r8voOs;^bZxD^JOg%_Zd>V)0L3P-KNVN%Mlfq0 zZo8-zp5igqEB+qx@aMGB!foMw{GVRm8T^sAiKC-Z`F!QISAm1uRLo1e&pS!coJkS8 zO5az%G?=K4%AS`!as5al{%@0CwH^Y6HT9LpJBnz7$v_uX$7QCn5I}f508HvDVEI&> z#xj=~-6!+OO}E|}&dBBCsv<%&-5bSwmsj?sc0cn;1Lc3;Ky}m;z|^s?hSX|0#QxLS z8^HPks6GasR~`b1c>tZck>R)!yiffWb$;K89ek6n;OFBKC_u%Od*jJJb@34RcH0Af z40AKWlknlm6d;=n3HY4>Ey_7nXVkSH0XP%t@i>jE!+PxzkPwjs=z84}C|>)%TbqeR zPtT1VWd;kFpT}Z+PMpOgtBMQsR;?1MS!5GfxbU_-`9qbvp8yxC=o}ol96N)`=+=wA;;T#r@ZLRaF_bcI^zLL zIiNCbTzMK$rw(;|97Kh)k{95%%2MY=B8EnC4taG5WjQq?{Mb%=4)gb9LG0e0 zop{rj7%TMXM|WrE2hmVX6MAN~l()p4l81lvWIw5)21C)*X zD;bORAMuS0Aa~>RaTKV!jrq`tY3SRJCVFulS-k@c4IP-d*RTA2mCvrYE+)?{h(py%32IY-XuG3bYzh)`NZ7LB&fCU&_>0HWHs!(t+Y@8d6Q42^ zXFjMqJxWelyX&Cu*dhr`Kd-n3iwwU0DkwThoG?Ov;wxLNDbaV&<{>1Ts}MlT zPO4_O1>fFwAoRHF7VdYBG5hVh5)g?1e#gD01Vje~Yp0&x{Z>+Vi?&CJ?4)swQpFJ{|a*a&Rjgn z$v>)M6-cc)RXW0xIA(MESPTtNa*wvyIzx*1sMRN(?{Ke^dvVvbHrGjp!K9@jiu1* zk%zbTRn}*nCt~>?IIjjI7A}vf#E47LGnYj^+#)|2tK6MoIXI0eIsdaYfxjy;)4xAq zqYeRYSEG_Mx>a}QcHm|h_~O05f)wjY?*Ry}GDmX8`@B?Z;L(ZG+b2z1W|8{&W$+xA zPD<Nh#GA@<2S(Y-P15;|vC$Mh*jYB!1l@c5IbaBY3wV`T3Ls!S{4Y+7G`VL7Y! zN_GEx8hrP2FSaRb`~yI=%w<@)l8sDKpwy+uoPAgMpn5iO-lCGb)wbAIPO)O1I1}p& zzaxnYEocC_G1sSxl6KHshy^DEDxBlkGOmdh3aZk86rZT?Mr;Mj3{wms4ZHM0n|bOUDg3joe?&6A0mJ$yZ5iQ z<0FUwwyXoXx;dhF!oNTmYD*YIiS_MqK!GCfW1Up6ZsT3e#sLiVM%dUhNHxB!YOH$2 z49>UCd-^4q>L;)<2?#z~AU5n7I-Vd}8y-|oDw@Ikvr~s>@Skj3UUC76v%Wt&Z%(Il z#0emF#cEC*cL!eZrCJ9(JG5_eW9mOU)3mHe95C#Fqr!lfLJJw^K+{&fxFx3~d}fC2 zgd%a{)5D30BLxl|YlB=Kd-C5#8n0ncYW9;Ds+3hYUnaSV1kc#TRAW)CwI*RCcX8kE z%g>5+EYz#*{&ml7p>N3WI^<%e${%2_yeVq;mvm`^Bb#6Rp9)!0Gq;j_xr__IQ;y)y z*PcA6e_H(=*g%csuDY_j%`Bg|&U~5;hKa5Z-41ztPq;`rhv&R^Pq^jiDt$$+-Zj}E zax*a`l_8|`WAk~{Mxb#S>G8SaNv(FUKAO(lc5TF<>rACODt9N7gj4Y2gU{R})FQvV4ajSek?bnNB zjL(UXt^WZ-oDQ!|H3a#x>z?GhO?Nss1ZQfndK{2%+SEo0|@ZfPf6$NFY6;%)UT7E5McT|5xl|ca+dKhGl+i`-qLV0CyG86OJ32Ni_;j#1pWT6ofdpd77TPwAMe%tyE_H}UMk;N zK(o)mb!QeOck|!dgQ^%cxhOW1V5&QyxmOh&mIw*juQ<z>-mCABFxCHa4q26dDX zVEV>zDgTV_;fCijz-Wok(J2&2I2*y4O$4bEzPUr z9LuGAQ$yme1;=EPqf(!dHzkd>2jQ@$q)NCAR?8;6=x3e1cjm1Kaa)-3qIqPZ(CbJn zMxWT5vKjhiF|LaG3kJK-q~V!4)2+MTjwraOgF1=0AZ?NF z0G}v4T8>{H&RY7v7X-MBIS#xQ>B0^Jc*ei0gD#(2Gd*M?F>BJ8D|*9euq4op8g+zseU$qY}kRPfdv1Z>ECiY zTrtT1p|EgFc9{@zWM%JhrG5F?ci`x$Ce7($4j<2rkg>|me?VMGFxfN+tQG)0H6v+q zXO@69NJ}ei1~C)h=3Cs!6i}4ASy$igce~(~$-9k|pQ)lUDWyjK#Mw())1`?`A<)XR zx6Mty)-vp?g3Ggv0^D*!j60Ge{htP9hoZdft4U$pPLfd;y3cGt3u3oY+n0J^TT&?z z=HpQQ=qG$^oz3?q{#`X_>|i>N!;FL>x`Z7xXwGD?xqA=~%nllKXI+*$zNJi857eBb z%KsF>%qzgG=IV?OW6cO9IcVeqIK`3!%WA+u<5O?qA)YHRoxfgArl#b`Z3bAdT6=bT z#1T2pdmVu5ZXN@7c6NF(Ym4LM;S+5&JXg1@^IV$Rm5h97;c3|e{DMBY%LWf4dW<;O1}k|2Bu@E&OV30a`AQt@Kvz?{6bGblN4xm1!_Pv(p?5X|zl3m$)@Nl?(PPEgTUR(%5p@F^_qF?2=nlVY(%FN>J#7 z64Hot?90huG>!Mh9*kqS5o3^}ou@xT?VINg8Sdn^lu4AyIv8@C`TUh&@}B2s-N>9| zDYsONadd3?mMI~}V^NwA_VH2tbJq-ukiW%A+;mLIYA=*-hmbi4LJ9TjMIfH{^(L+5 zQl)Qv$Bje+&iuTQV#Br9+r#5uAxxVRZ@ckpdHL5VS%tTFEcN>3nQ~7=EInVO#LZl) zvz{gTSu!fm!Am=(1+_R5CJ_0xRyyXy zG0r}=G{!2NCBEWOMegnbaZcKg<&oWqlejs5eWm@2)wrg}FRWK(sd2(XXBziu9m+@+ zh`;x{Q)`CAmBqigLH{k0U(WAz4CKT_n=0la7ZZE0n;7CfrdS7@j-&X2DdUmAGRLUL zDBbAC&HQQxw__Y{d&~i?goQ>U{r8&X6}N2q`syy90>@)9qyx8?Nj=Sg0iifqW>pyR z0T1_hCiD+=46mcu4UfH;z&-3VyH9MUd<{Kz@-5N(BT;?#%yeK$k0Be_h+uRxD_9aG zWJ8|Q46$rM~3sqZAk)t1}$&B=TiVd%<5)l3Th4tadV8vVY z`^Cx07-rvDp6QY#aoq2JjGY&XaUvIRFZ*Yh`s-enXV%hIBP`GCNRkBa_op6eZnH4* zFK{;XF59njbp%ysU8kmSVG6*o#N^E$-pH#gNYMk76O|CRyfgGNIRHiQ=$z_KoJI z#@oV>ZYfxgS0t});-!!Yh(o8hhP|MKRLH;Y$a#q8#VEoT%xJ-jix@+v_kg{ig;d}o zPP1UBSI_{KC@GQ`^!L{w*fB4{TNyovaC3;TkApCRWdl?&=;IM8TmexkPzz$bEi7q? zfT&j?v`JwY^trHZR!B?;hQ3@t)i;=lI<%Z5_gS)Bz}O?+dbxnLZ?O03&}Nn>Q<4|Y zAiVep5={E8ICVb71XNzi97$`?#Li)nABDGZn1uW!HzTO}lTbu-gS8pBXTTo^u>dy< zC)aoI^0vEy8||IJrxh503sV%`nwRF_l5k$oF>#3$#hIT_sP&rRxM%HAMeRk}MQUm@ za>E@=VDEx(Re#qrs5=iDz?-sa&8(~j{T4)b8t*Vzqr>YGC=NT0*7Slb!mMPSn~&Guczq zCbg9e#lvp8Q#;9|G!k^X+m|-da+h|a9O;g`dPGMrVsa~ywIN(WHk>o0QVpfQ0}>pO zkvk2M5*Ay-%U?^a1J|~_cJ+w+G@43$44(XVc9^-wqm6jnyv}jE4_`1xo)G zl@ikm6{wg9seXSmgWc(?%N1uIH)M}dm|LNIKYd2Z zdfmmrDmlZhVOUm~Lwj#5{L-(*a&7RmJXna>uGOw#lT2gt~fP zP?PSzH57hN6=ZU*=EZA;Ih|)%zPa6O2#)-)lM>g|5a^;ml*4m=U;e>|Y{}dE1m{*m z+zmNPvCNC_aS<6}SA~>+sJk=@E{G4Dyf&x@0!wVtqnpK54h6GtvBrYwTIxkTrMv4e zuuSnNto@fBLgumvUUt%LFIiB30~?InOIV4o$TJV;mq?7!R!Jdq7=fmUFmu=;K3*w$ zn-KIlF4lqn5-sEwGw>tq5Q4{r(WU@>eh-WL9)(cA5;KGvJ1`&ijufv2qfH;``2fp< zmB9QKJMdRda#$DaUGgpeDjy^bCvXz>jtb8Yv&|Xm`Ler?0BbQ|Net428+Zddq{E{E zGwJ*Pz&B2DSe@J$|k`HQM@_p|w_iHCr;%TQCE|ZJ6e?as#T=sTo1t|Y* z$s$B?X(JI(ix*aElAGEnu`v-?Z^$=s#SUzq;9hRC z>H2PzwNB9<9~l3$gHOS*?4jE`mwZK2^2ADen)eT&p%f^IBtuHx*>It^YH^{tKxhcr z)OH_gCw-H*+26N+cnB41(NL_|;wTzbn5l3E|i9#oP;xCz)r!=b9Jsp9u(^pICpf`+RoW z9v$;Rnon$vNx}9alE3haAcJ0C{xu#Z5 zd8zSM-J2Eni1JUu5za@SB968SBkt`fi1OZ?Ia{E7&Q_GsRN_cx^`x{6MbEDbst$hrLQ^SIE9$~X z9Wlr44_WjBNS-l;3Rx=kUwm7ah+$0rDxsF45p2G3aU)>BekBL^OFmu13KABJiDe-0K|S$QmIHyRc_9tEpE6o;M> z7lMTz_yh*V;8dVVkwaf#VmbRm2_aY*fwC~jJv?4?DHiApY^-uTih6%2Ipog00*iWx z*B3|x4C%}OU~28rP$?18T0KSk~fSekKDL8!)hy~%M{zZp0oOr*aBdjj;!AY)7hlSs%J3ecnm;zfqV04#)G~0-|R-M50Nt-v*?!?{;~s z$(^rd{M{}5t)KY*t)2tmJ_`)n2<~*CBJVc&jU1Q)p!IVuAXDKQvVBDDJ_M|+bfDA{ ztxy;5>%0uL10PotwsxU~pN1X5J#TSa8st$P*JWg@VEKiA?nrf%7-0H*Qm3Y1VbHns zh>k;4>^DkPBwG&{&P25qZic@(rXIbfBYdxS+;$$W;I_w*D9lahqcSRSs_t-Oo+;ek zBC@vo?(4_A)s_#|mxEWOFAZIdiKw2|iHEvF6-Vec-Fbfv*o&r6?9upD|oXXF=`W&_zH-(kQYPvX(f(})(Wro(l`tH?C zHTErXiRpI3YOOt%EXg~(1Z=Lq!)aD|r=adpjU- z2%}P7*&s-eQlqPWV(dG|dwK4`k&yN=fu0?HxT{M+(rwyCFHt5RZg*R`0XkUZPk+Wz zIR1Vfs(0rbOXC5&(1 z{0lgwb!aF0GJj7g!G=QC5CzyOp6Ud`apIaL%j{A#S0;QC~JwSzKkdy z2`KP{vSmgQ`AbX6V?cw@uqgbYSlHZQBrwQF#OG*Cyuc_|^C#$mEU>6gw6u))kQ{o8 z4)j1VL>LSfg@?z0F7*_OhmCdTGK8#Q1ipS!8fGz0w8!*johzYL5H;GrlgQ*=@% z@}d7`5D@l!+6?Dibe+RE0wTf>>Zsy5G^m7tM1d-WChF_?FZ+4) zu48#+)#ZXWD?u~2NEeUL#D+djC-3R`?Er|rX2tmYR#xqLP-AMH=%SnSzDvCAc*2J# z9_iJyGY-41E*DjV^WqgAz2ezM`zA-fT`Ven=9^RFzc))#Snj$0U36L0wQ9`H=uy8x z+95=*BeVppmfw5qez(^b+LF-D6gLvYpXQ|ca+a{#;25JSp3=MFKkjn7lDd9bcA!lx zq5u6HexmR7bDv`SXV*bAvCzU3!UnJ0w5kdf+9tm+`<~voFC@rf0#$DHSmMetGh}BG ztIV76<$(cP+sO&r?OL<#?mV93Ze2fZ1>chBQPf!gXwMM3bT*hD|HWO1>58L?>XMN7 zCBn7u=yf~gu#KqE?M?m+X!5De6~45se3kC@?vu#X7uM}KV?p9a@HMq*B@SmZ_n+}X@v@!lsqOY(cWWs9rwx(9g8WBwtX9>&8eKpnOMtTaB>qK zwF&Ubn@ly8H60o^H~pR6I9_4qicM^C(KES*OZN2D2x%t{!F5f__YJj0E)Ex~`x6iP9lahpAb#|x(@-kUHj$`E+g<5P#B|qXL@($`(VbOe(Ck=MUdgGFMUL|$) ze+FlXJn8BG0FFGBHACZL1V>oa^E!4Zu(dY(#*_A0z&FHocs28yh}B{j2Ef|IE% zmHOAfr>qpf^~AxauI{Ep%|ZBNpejUou2}6)0-h-=A(Gx=m}MaPWdojRN?U!0C9y-E zK0r)km^HwXxFJvR5fqqay&Zg3gRmrih?Q!$3KibTQ;Vkrh-6H&ZCH{hfD!m;^w_X&bVv*uf&-2IA*>sdJ{AvZ5g4V24RJw7SfJ5Ut^H1NL!+mIy}*O; zVj$ws=$T7ZA&CI46Z| zFN(t9C->#tr#d~EH0$F))^lzFZ-X^B15gwX1hJ;Bou|LRH)X%Dtx<`^?w3@^co>Ke34!Zl#LC)i!7ariY+*8LD_pMEFJF{{> z+|&J@kS4Gl74Oj_qq$MgqaPwxY~7F&Bc@DC`zSl_J>AoM+Ly5kIA<{m`;iF>zLaeF z;&Vzb7p)8WFE)$|wU zX-&j$-LMa28`Qf*qp8QH17X>R5jZQd<3o8HY!7$&c$r$tyjXHFV=W7^;^GiOeYuWVe56GIk0og_Ey>15j|n2_D4_3)36?Cl(wzV?jL8 z9t;jy@Q=}Plsr1eFLN5f^{VvO(_iR*2Nw+Q6^-8i2H@6c*mjUBg9~!z%7@j1Q_nS& zI>=SJ^;B4i%z5d}`9SgT-3pqv9Z9O-Jdg0f@5&j=pmuM{B)22yTKzxuzW&c8+$6bV z&Iu`bed+02f}*P=2Yd@V0B4QxBh#I+Jt{RlF2dQeRr6Lqx6i(3GIC!qqh}&+9%~^E zjMn$7s^@6R&1xD?W`g&*lojf#KOyIh^UvHplnFgN)a>{7iQL=g`EGwS>Q+Y1Mbi%y zM=QW9&n4W9_AB3I&ux|vmiS&YS%uGRRGiPA76^FHH%}yK3mKaCImWn8vs)rhzi7+! zRDXAlk_o3W{F=g`q*X~TBalg6_7pJ`O-zt7j_Mj+M{jH&3&4G|)-mwZ>kl8L@LP8u zufsjb?u0b+<0x%T?IDwPskVS!_ZcX2mGd6iN{Sw*oX6vXg0E@Ij5KFF^@w+m6q z5)90Pd~97GvebRFeaFIPCJV)EH)V=mNs1X<5L{gk_hg^ZU7}tTw|G{y_#Z@eY=0NDu`FuxiIjm@~Z?JP3GunOT*W{*diy&w7shKp;!u@ zRfD3Bf0DKn-;L3Wb%Pz6kO_~Y2<&%3-`qPHLnbnpX&x*QM^1QkVR*`U_tt6C$eDWI zinyEF|JN7FfMU;x*L2!^zIh3k**gOC0Mmc0xfHL3H4*WyjLU;-u@3S*20-1}D@obM z+5D%rj`90?ZOQp9!8i_QDqQ3B2rAKehmh{~sSiKSZxH{QzqdSEd=J;B@rF*Vf?#TV zO#IM)BBf4vMC5NW%x+}+=|o#CU4?;5OF4l)Q+?3~{_s8tAc1QmM|He^rP~Bsv(oTr2?9?OO^!=%|VH{wcN3 z3IHc4q5vd|ZYo!S%9n}w_un|r3~s*;`t&@DWQG3%b?O)RC!g)LtqBcUGArJ3rzF`q zi>>@DK>hyQCe?`uUZ^nZ=e^6YL2{J&kH;7yx!M{i_886Th6yD)k1D&Y)`9Z`YL=t& z|3mVpszN1lvK4lrzuq(flue7kN*=(49SYuXB%g6bCuCUbWNX!Yx!O9Ie{e7aMvt6G zg&zrv47m-9gbfW}p0|+~UB8YZ(`D))6U5n#OQsSsETn%!tab{Z%zvVa_a)%r=qGet zvPGdR3@Ebykk05t#}ZN#jZI4=H+g_xBp$Z6WkNw$bfB-C%4>n;CIH(dEBW8W zI@Dfy1uZJ9)B7n%YcUZX!8N^tfb?FO4-)#6+RF42MJC=7#UZRYmg6c};zhRi_w?O0 zZQ!`NbXuaH&WpDdMfA=zZH8E2`ys>r(mtW_n^Iu}Br|+UuU-tyo?sME>46wa^#l#?3FH?roFv zYty3i&!Wb;=-8Z&MDn+ZBs(8G|Kb$&(RG-+Y_;!day{)$BFiv}@fN_ohByC6!Px*F zY&%~?x|RXfrPU}OqbBuBxFm?rv~(_Ogr7+|Qk?(387SFfaB@A8SkmR(gFh_5yD@Na zfBt&Jea@qr{lliJZGH*+xJWfNa!hLaS?gDQF@F+nd~qejg**jp@tJ1%KPT>T zt$zKIQ&(>HM6=^z%=9w_-pnLsCPbOeBZ7$7Yq}v#oLyc;Ou{gYM?$7q_}+P$F;z1K z_$}GFZ<6O%awp0=HS1GoK2e^iUhgF(PGmEGz|6L~V$HHTdc#gUV;I-(x7@3b zt15J#;J(8Oa$eYk<2pDCfe@F@BU-Ujo8rzDWi7QAH*6gu;FM}&Ei`0}0;@#1KVJ~F2l z;M`luo@BoHwFpZ!RS7uQjL$iH;vn@yF4vpP!y4KB7bI!A<1ok4Eix9qN5!t^2tTeiO zHEEVbPuN5p(v679&9vk;t1;OdIQDe2IKvA5ij-K&)mFul&g1InoCaOjeY3u#B5h6) zHzlI@AWP<@HGU>>8l5}TZpY!fFjKPAxan*>R-@YBm+CkyS7ules$UHwGfc=2_2n_X z;=AytOF1rP65ipQ{E=~?rxD{(_xVe4RnB|U110efwRRSN-b{8QbsOY;MF!%}-zBj7 zE;1;0p0So5y_MYNg`;L%OlG~m2s*k==)N-#JEUk1VUv;9GS~f6(Do*EVY;Zta z4G)8e_mMg%dIXN~9zFdzFS3Aj`J6ro8|x0=!M7sa(D6c*K@q$*}!td1k2k*h&Z)%EqFU@6sGAFiv7qBk}jwVS1ILn1Sk0vuy=rNjolEtR|9+uR`p#+VB)effwX4|lyx9PGY*Tf%W2M$(+REA3dUbAiHcPWp zyx1(LCjP+?cPBdJX07-4*8b7}-&#}w_P%_Nl=Xr#YS^$Of1hdb$R>;ub&cbZUu)e% z6j@)A?lJf6y*}C6#KLvVT(#K3jDxvk^-V|m1Z_Rvdd5A4x#mZ;ebF?HhCT`(3eP1n zw)WWvv~TvERw8Cg1OvBQ4E|<>#k4?dWMuIedRU$ z8;Rx)Z~M8sW=4`sBdT+GT8&%?RfVC<*1jtsg(xvCrm0I@Y|T-)`o_v*d)ay{TFP#Y z@nW$s;@T}jbZ?o|_PWKcm|}8vY}oLm#5^QMduN7!hm_dT%igTY-RSm}*9UL*9mKi# zT7SVoNpdTH40E;Ja>nUk;_+Ix!kp&&CJr^<9ruf>MoHa#C!aZS^WP@!8x4kY$Njk4 zvlU&%l{jVEm3d{ARvxSxt~dj=g9OMgX;eHtDL3mz!Ur8_cBloO@{t;<*>7ENw}$($Zfz;Rwps;y)T~5y%K8qn309BU}ooP44FPO z#87%+AUp7MXI@PsR-h6BEyluAMsGI&)6heUaq#-+EhaD#*hIYS80|55$pYwR>M%W0 zNCGy(9o@_frbkg-#;1(gKI8x6IkcDnuaEII!byto!4nG|JOmSlSvXAZ2}I`}!V<$Q zJ?@ld{RYym#Jc5^dZ319mzxu*&%xx|Z_B!6o}jEqS81YjorJA9(p8)kBVpAOcgkaz z2TtK<^6CH1I&Gey+FhK=mZ<-nzS1RQcQ4VaV$)detMo?CLZN7nr!LXq?vr0y{>)w( z=@v+1?t9O$F{OX|zv6|C z;p53+N{{=$5`>OD#QTIPy^fbWf}Zsr#zG7Ej*B=(&+=RQJ9&sy$_j+B&_itT5DXYu zAutw3$o30#Y|R%?t(YiQCWx)#-;JAiX@P)OFQH?^c-Yv|asjV2lp2y#l;GmR4!s1~ z4=;b1e&*>U$qpUAqSSxCZq;!wQna|X6pc<8>2`cpEFH8{ zXlUS|SsHmPU*GU@XKg!Sp?%zU>7bNWUBO8Fm$q)1Raqm$M>Rc$NZOiH)P=lmCijZF z@5NkxxOe%-(#1Txs^Qegoi{q`cILWZv`)on)%EnyHa08x2~84q;!2f>X_(KJ>o649Xc#QT*s6`Mf>m3m#_Q z{2m-Qisjq?*33^S6gGj^fA*L;ddV|Ja!}a#uK16;5WduE+>42 z+6G1`R&9R+cfOY^jV<7W;7|Tm2%$0A^WCkvF@bT+;|Df_CMao%Kn2@g#&FN<2>FdM zBu7g*#~@AzMUi>_i8%X8G9u?MR+iPjxaJu^R`?^J2FAI>C&o{M)hSn3+^f&wVZ=MF z*P{Tg&jL%hSeL^RP*(4{Im%6^H|t~C-Soq^mb|7(eZorIbW$Ux^BA?he7O^*sF&hm zYXUnV>gTimanbF}Z!%dRI$19x<`f4;7_7IbOO@sS^2Us9y_Co85I}b}h8a=OH%dTD zqn#RQ`WeE?$5MWk3M7>Z#FbVH8yDfIcO;i3%8~5;vKd8NUCdwAxTCrKzHR-y51btD z3|uLLR^yP{_I0MS6-f008Il9wUoy)umjDia@@$@m1JN^b5tXL2@55&#+}tg1%!j(o zOvN1z4pC;+t1WHkhv{1pFAZ^p83a3?i-dBaO&+c4*zrou&lld!ie zvTK;{!_<1%vwRa>-~Cd;%bNRyv01Zl)vt-{5(Z@ zwr~1I1R8dwD3PaPU$UM|n^E)k5((Ah{~{S4G85!~pQKjh_5^Y9=gEgxTYuqA(N;eP zg{UzX0gYJCMWTVP`KA+3jFAmihkJ$s329vdGCKS2_^S1Cv1w;t&Gm+`Q{vXDBbMo7 z1q$awsSCgK(J+22j)=Zt7LoV0eU~FXw;0{x6!vuW`d!y$Rbozm=7rsm-Tg6PldP&D z-bJ78QYfwd4peoOfHmx&Cg^XnHWlL)7!+v_5Kh3Dguv0;| zI2-UG24pg`76J-I&S-eFXl>ZgOEfHI|0Qh599ke9>>UKlBCk zKuOrUpr|7}JTCM$X6a5=s3$g7t^X1kggVF)uv}r>x?_*gjmQgqbEO9|@b9>FHaRNc+0tKD~ zG4D&vHaqD~2k51$y6Eb2h!}3*P6t(|2g$pZ2JHQ^Up`gn=V~GQ+zgce9IjPBu*=M4 z>vMQQ$&WD|EvGwDVrE$N29T`!29+tf3Up=&eG<$tp8?#@QcX`C$9FLmrgVMcw>?`^ z=ib`s`gDALU4JqI^+{yP?3)Q3+BMGKk8jLvwmFbEl&V)4!#vMewp%wDK$%7U2mfxZ zxX9WWlW;ONoc2)5qO30YTCtcnYG(FDEac1J) zA{q=axnqokIxlTJ8u|D-tcGsif*GH!d)})#y?OWNsue?K>cM;@e~w+k{3w!qo4Cg2 zL;K^QPOPI0x%=0eKU|hcm#C7m8Qz8UE(Khete*yW2qoh#81IQ45+ryn(Z~Ywmg-9b zY+<+yK9;netu~th7aw^Eo9KR={PZd5$2@>fc`Y@|tp{8nnDvNC=dh%qgyxXS7J*M;5myWwlwjlRSM^R0R=tFSF4<_ z{wYKsF>K`tK!Fq}c#;JQK4C>gf5O-{6Z?VtmgVby8>Uax7YrMDDD;IaIuGL`dPZUh z;yXbUQsw)}!PVnC{Hx=Vn+PCXAME4qJyX7MQsso+2<@o!qrgbU9tITg)jwwVx3MBr zBH8+5y#FS0Xte4|yhIQyor8eKaHPNV>4e}b>*$a2%Nu6V{ynzkP33-LKgCn3IxeVb7bA(x^gIFu9ada9 zlGxW=ISgWC?3H)45MI^>?}YyDJYL=L_El|~+}zne(Jfo(*+dkVKA%s67>NoW27G9O z?zc@209!&X!Ca;EcZ6;R!08YLFD>COY8sP7NpcOoOS#H?19;r=V&W+#lqGgr7f*h* zJ_ff^afNed`nhV&M6a(R`)(?yB-@2Y&2+8Jq7JQ1Q}OW)Ri~1q%KU4fysFm4ZOP?E zjhI%$Z=bj2nCzr$gqwLFzRRG6o95;+xz{u@JSyTq;tjC(vuGsvrgyjXASN!h=%6fa zKO`2zg>VW}ca2So(NG6xwnba|n`5bcTyZ?55PX<+#ysuO^?X^ps!VBgO3Pz&r*li! z!mI4aS;HoNy|?<$>MO=S=EgilW}g4Z2BJl%lk5&I_gPp!OcI_C5;rItm)`W3sv2{+ zH`mBm)^_j~`m`Cxi{lTmiTmVD`BYQo$$pldVT5OVKS#Bj?VTvr&0Va&pD5`1w&q7= z-H?6oLfd?TtGbiRm|#LIxT>Fs-0p4&hnj)yRA;<7)`IVY0lig3OM-q2qSZ4Cc@mi)`kh2Uejp4ht-%g z{`UTTX?#3&yHmWnvY*fuIlr6n=Uhp6^ham97r%1Gm{o+&NE_+)8r{ge=KJZfLZ^

    +WdEKc7pJA5c?-2Fn62X`RyGH?pc)`iqA- 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/types.py b/libs/win/pydantic/types.py new file mode 100644 index 00000000..f98dba3d --- /dev/null +++ b/libs/win/pydantic/types.py @@ -0,0 +1,1187 @@ +import abc +import math +import re +import warnings +from datetime import date +from decimal import Decimal +from enum import Enum +from pathlib import Path +from types import new_class +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + FrozenSet, + List, + Optional, + Pattern, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) +from uuid import UUID +from weakref import WeakSet + +from . import errors +from .datetime_parse import parse_date +from .utils import import_string, update_not_none +from .validators import ( + bytes_validator, + constr_length_validator, + constr_lower, + constr_strip_whitespace, + constr_upper, + decimal_validator, + float_finite_validator, + float_validator, + frozenset_validator, + int_validator, + list_validator, + number_multiple_validator, + number_size_validator, + path_exists_validator, + path_validator, + set_validator, + str_validator, + strict_bytes_validator, + strict_float_validator, + strict_int_validator, + strict_str_validator, +) + +__all__ = [ + 'NoneStr', + 'NoneBytes', + 'StrBytes', + 'NoneStrBytes', + 'StrictStr', + 'ConstrainedBytes', + 'conbytes', + 'ConstrainedList', + 'conlist', + 'ConstrainedSet', + 'conset', + 'ConstrainedFrozenSet', + 'confrozenset', + 'ConstrainedStr', + 'constr', + 'PyObject', + 'ConstrainedInt', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'ConstrainedFloat', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'ConstrainedDecimal', + 'condecimal', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'FilePath', + 'DirectoryPath', + 'Json', + 'JsonWrapper', + 'SecretField', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'ConstrainedDate', + 'condate', +] + +NoneStr = Optional[str] +NoneBytes = Optional[bytes] +StrBytes = Union[str, bytes] +NoneStrBytes = Optional[StrBytes] +OptionalInt = Optional[int] +OptionalIntFloat = Union[OptionalInt, float] +OptionalIntFloatDecimal = Union[OptionalIntFloat, Decimal] +OptionalDate = Optional[date] +StrIntFloat = Union[str, int, float] + +if TYPE_CHECKING: + from typing_extensions import Annotated + + from .dataclasses import Dataclass + from .main import BaseModel + from .typing import CallableGenerator + + ModelOrDc = Type[Union[BaseModel, Dataclass]] + +T = TypeVar('T') +_DEFINED_TYPES: 'WeakSet[type]' = WeakSet() + + +@overload +def _registered(typ: Type[T]) -> Type[T]: + pass + + +@overload +def _registered(typ: 'ConstrainedNumberMeta') -> 'ConstrainedNumberMeta': + pass + + +def _registered(typ: Union[Type[T], 'ConstrainedNumberMeta']) -> Union[Type[T], 'ConstrainedNumberMeta']: + # In order to generate valid examples of constrained types, Hypothesis needs + # to inspect the type object - so we keep a weakref to each contype object + # until it can be registered. When (or if) our Hypothesis plugin is loaded, + # it monkeypatches this function. + # If Hypothesis is never used, the total effect is to keep a weak reference + # which has minimal memory usage and doesn't even affect garbage collection. + _DEFINED_TYPES.add(typ) + return typ + + +class ConstrainedNumberMeta(type): + def __new__(cls, name: str, bases: Any, dct: Dict[str, Any]) -> 'ConstrainedInt': # type: ignore + new_cls = cast('ConstrainedInt', type.__new__(cls, name, bases, dct)) + + if new_cls.gt is not None and new_cls.ge is not None: + raise errors.ConfigError('bounds gt and ge cannot be specified at the same time') + if new_cls.lt is not None and new_cls.le is not None: + raise errors.ConfigError('bounds lt and le cannot be specified at the same time') + + return _registered(new_cls) # type: ignore + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + StrictBool = bool +else: + + class StrictBool(int): + """ + StrictBool to allow for bools which are not type-coerced. + """ + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='boolean') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> bool: + """ + Ensure that we only allow bools. + """ + if isinstance(value, bool): + return value + + raise errors.StrictBoolError() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedInt(int, metaclass=ConstrainedNumberMeta): + strict: bool = False + gt: OptionalInt = None + ge: OptionalInt = None + lt: OptionalInt = None + le: OptionalInt = None + multiple_of: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_int_validator if cls.strict else int_validator + yield number_size_validator + yield number_multiple_validator + + +def conint( + *, strict: bool = False, gt: int = None, ge: int = None, lt: int = None, le: int = None, multiple_of: int = None +) -> Type[int]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of) + return type('ConstrainedIntValue', (ConstrainedInt,), namespace) + + +if TYPE_CHECKING: + PositiveInt = int + NegativeInt = int + NonPositiveInt = int + NonNegativeInt = int + StrictInt = int +else: + + class PositiveInt(ConstrainedInt): + gt = 0 + + class NegativeInt(ConstrainedInt): + lt = 0 + + class NonPositiveInt(ConstrainedInt): + le = 0 + + class NonNegativeInt(ConstrainedInt): + ge = 0 + + class StrictInt(ConstrainedInt): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedFloat(float, metaclass=ConstrainedNumberMeta): + strict: bool = False + gt: OptionalIntFloat = None + ge: OptionalIntFloat = None + lt: OptionalIntFloat = None + le: OptionalIntFloat = None + multiple_of: OptionalIntFloat = None + allow_inf_nan: Optional[bool] = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + # Modify constraints to account for differences between IEEE floats and JSON + if field_schema.get('exclusiveMinimum') == -math.inf: + del field_schema['exclusiveMinimum'] + if field_schema.get('minimum') == -math.inf: + del field_schema['minimum'] + if field_schema.get('exclusiveMaximum') == math.inf: + del field_schema['exclusiveMaximum'] + if field_schema.get('maximum') == math.inf: + del field_schema['maximum'] + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_float_validator if cls.strict else float_validator + yield number_size_validator + yield number_multiple_validator + yield float_finite_validator + + +def confloat( + *, + strict: bool = False, + gt: float = None, + ge: float = None, + lt: float = None, + le: float = None, + multiple_of: float = None, + allow_inf_nan: Optional[bool] = None, +) -> Type[float]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of, allow_inf_nan=allow_inf_nan) + return type('ConstrainedFloatValue', (ConstrainedFloat,), namespace) + + +if TYPE_CHECKING: + PositiveFloat = float + NegativeFloat = float + NonPositiveFloat = float + NonNegativeFloat = float + StrictFloat = float + FiniteFloat = float +else: + + class PositiveFloat(ConstrainedFloat): + gt = 0 + + class NegativeFloat(ConstrainedFloat): + lt = 0 + + class NonPositiveFloat(ConstrainedFloat): + le = 0 + + class NonNegativeFloat(ConstrainedFloat): + ge = 0 + + class StrictFloat(ConstrainedFloat): + strict = True + + class FiniteFloat(ConstrainedFloat): + allow_inf_nan = False + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedBytes(bytes): + strip_whitespace = False + to_upper = False + to_lower = False + min_length: OptionalInt = None + max_length: OptionalInt = None + strict: bool = False + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_bytes_validator if cls.strict else bytes_validator + yield constr_strip_whitespace + yield constr_upper + yield constr_lower + yield constr_length_validator + + +def conbytes( + *, + strip_whitespace: bool = False, + to_upper: bool = False, + to_lower: bool = False, + min_length: int = None, + max_length: int = None, + strict: bool = False, +) -> Type[bytes]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + min_length=min_length, + max_length=max_length, + strict=strict, + ) + return _registered(type('ConstrainedBytesValue', (ConstrainedBytes,), namespace)) + + +if TYPE_CHECKING: + StrictBytes = bytes +else: + + class StrictBytes(ConstrainedBytes): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedStr(str): + strip_whitespace = False + to_upper = False + to_lower = False + min_length: OptionalInt = None + max_length: OptionalInt = None + curtail_length: OptionalInt = None + regex: Optional[Pattern[str]] = None + strict = False + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + minLength=cls.min_length, + maxLength=cls.max_length, + pattern=cls.regex and cls.regex.pattern, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield strict_str_validator if cls.strict else str_validator + yield constr_strip_whitespace + yield constr_upper + yield constr_lower + yield constr_length_validator + yield cls.validate + + @classmethod + def validate(cls, value: Union[str]) -> Union[str]: + if cls.curtail_length and len(value) > cls.curtail_length: + value = value[: cls.curtail_length] + + if cls.regex: + if not cls.regex.match(value): + raise errors.StrRegexError(pattern=cls.regex.pattern) + + return value + + +def constr( + *, + strip_whitespace: bool = False, + to_upper: bool = False, + to_lower: bool = False, + strict: bool = False, + min_length: int = None, + max_length: int = None, + curtail_length: int = None, + regex: str = None, +) -> Type[str]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + strict=strict, + min_length=min_length, + max_length=max_length, + curtail_length=curtail_length, + regex=regex and re.compile(regex), + ) + return _registered(type('ConstrainedStrValue', (ConstrainedStr,), namespace)) + + +if TYPE_CHECKING: + StrictStr = str +else: + + class StrictStr(ConstrainedStr): + strict = True + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# This types superclass should be Set[T], but cython chokes on that... +class ConstrainedSet(set): # type: ignore + # Needed for pydantic to detect that this is a set + __origin__ = set + __args__: Set[Type[T]] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.set_length_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items) + + @classmethod + def set_length_validator(cls, v: 'Optional[Set[T]]') -> 'Optional[Set[T]]': + if v is None: + return None + + v = set_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.SetMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.SetMaxLengthError(limit_value=cls.max_items) + + return v + + +def conset(item_type: Type[T], *, min_items: int = None, max_items: int = None) -> Type[Set[T]]: + # __args__ is needed to conform to typing generics api + namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]} + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedSetValue', (ConstrainedSet,), {}, lambda ns: ns.update(namespace)) + + +# This types superclass should be FrozenSet[T], but cython chokes on that... +class ConstrainedFrozenSet(frozenset): # type: ignore + # Needed for pydantic to detect that this is a set + __origin__ = frozenset + __args__: FrozenSet[Type[T]] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.frozenset_length_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items) + + @classmethod + def frozenset_length_validator(cls, v: 'Optional[FrozenSet[T]]') -> 'Optional[FrozenSet[T]]': + if v is None: + return None + + v = frozenset_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.FrozenSetMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.FrozenSetMaxLengthError(limit_value=cls.max_items) + + return v + + +def confrozenset(item_type: Type[T], *, min_items: int = None, max_items: int = None) -> Type[FrozenSet[T]]: + # __args__ is needed to conform to typing generics api + namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]} + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedFrozenSetValue', (ConstrainedFrozenSet,), {}, lambda ns: ns.update(namespace)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LIST TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# This types superclass should be List[T], but cython chokes on that... +class ConstrainedList(list): # type: ignore + # Needed for pydantic to detect that this is a list + __origin__ = list + __args__: Tuple[Type[T], ...] # type: ignore + + min_items: Optional[int] = None + max_items: Optional[int] = None + unique_items: Optional[bool] = None + item_type: Type[T] # type: ignore + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.list_length_validator + if cls.unique_items: + yield cls.unique_items_validator + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items, uniqueItems=cls.unique_items) + + @classmethod + def list_length_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]': + if v is None: + return None + + v = list_validator(v) + v_len = len(v) + + if cls.min_items is not None and v_len < cls.min_items: + raise errors.ListMinLengthError(limit_value=cls.min_items) + + if cls.max_items is not None and v_len > cls.max_items: + raise errors.ListMaxLengthError(limit_value=cls.max_items) + + return v + + @classmethod + def unique_items_validator(cls, v: 'List[T]') -> 'List[T]': + for i, value in enumerate(v, start=1): + if value in v[i:]: + raise errors.ListUniqueItemsError() + + return v + + +def conlist( + item_type: Type[T], *, min_items: int = None, max_items: int = None, unique_items: bool = None +) -> Type[List[T]]: + # __args__ is needed to conform to typing generics api + namespace = dict( + min_items=min_items, max_items=max_items, unique_items=unique_items, item_type=item_type, __args__=(item_type,) + ) + # We use new_class to be able to deal with Generic types + return new_class('ConstrainedListValue', (ConstrainedList,), {}, lambda ns: ns.update(namespace)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PYOBJECT TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +if TYPE_CHECKING: + PyObject = Callable[..., Any] +else: + + class PyObject: + validate_always = True + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, value: Any) -> Any: + if isinstance(value, Callable): + return value + + try: + value = str_validator(value) + except errors.StrError: + raise errors.PyObjectError(error_message='value is neither a valid import path not a valid callable') + + try: + return import_string(value) + except ImportError as e: + raise errors.PyObjectError(error_message=str(e)) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ConstrainedDecimal(Decimal, metaclass=ConstrainedNumberMeta): + gt: OptionalIntFloatDecimal = None + ge: OptionalIntFloatDecimal = None + lt: OptionalIntFloatDecimal = None + le: OptionalIntFloatDecimal = None + max_digits: OptionalInt = None + decimal_places: OptionalInt = None + multiple_of: OptionalIntFloatDecimal = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + exclusiveMinimum=cls.gt, + exclusiveMaximum=cls.lt, + minimum=cls.ge, + maximum=cls.le, + multipleOf=cls.multiple_of, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield decimal_validator + yield number_size_validator + yield number_multiple_validator + yield cls.validate + + @classmethod + def validate(cls, value: Decimal) -> Decimal: + digit_tuple, exponent = value.as_tuple()[1:] + if exponent in {'F', 'n', 'N'}: + raise errors.DecimalIsNotFiniteError() + + if exponent >= 0: + # A positive exponent adds that many trailing zeros. + digits = len(digit_tuple) + exponent + decimals = 0 + else: + # If the absolute value of the negative exponent is larger than the + # number of digits, then it's the same as the number of digits, + # because it'll consume all of the digits in digit_tuple and then + # add abs(exponent) - len(digit_tuple) leading zeros after the + # decimal point. + if abs(exponent) > len(digit_tuple): + digits = decimals = abs(exponent) + else: + digits = len(digit_tuple) + decimals = abs(exponent) + whole_digits = digits - decimals + + if cls.max_digits is not None and digits > cls.max_digits: + raise errors.DecimalMaxDigitsError(max_digits=cls.max_digits) + + if cls.decimal_places is not None and decimals > cls.decimal_places: + raise errors.DecimalMaxPlacesError(decimal_places=cls.decimal_places) + + if cls.max_digits is not None and cls.decimal_places is not None: + expected = cls.max_digits - cls.decimal_places + if whole_digits > expected: + raise errors.DecimalWholeDigitsError(whole_digits=expected) + + return value + + +def condecimal( + *, + gt: Decimal = None, + ge: Decimal = None, + lt: Decimal = None, + le: Decimal = None, + max_digits: int = None, + decimal_places: int = None, + multiple_of: Decimal = None, +) -> Type[Decimal]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict( + gt=gt, ge=ge, lt=lt, le=le, max_digits=max_digits, decimal_places=decimal_places, multiple_of=multiple_of + ) + return type('ConstrainedDecimalValue', (ConstrainedDecimal,), namespace) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + UUID1 = UUID + UUID3 = UUID + UUID4 = UUID + UUID5 = UUID +else: + + class UUID1(UUID): + _required_version = 1 + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format=f'uuid{cls._required_version}') + + class UUID3(UUID1): + _required_version = 3 + + class UUID4(UUID1): + _required_version = 4 + + class UUID5(UUID1): + _required_version = 5 + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + FilePath = Path + DirectoryPath = Path +else: + + class FilePath(Path): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(format='file-path') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield path_validator + yield path_exists_validator + yield cls.validate + + @classmethod + def validate(cls, value: Path) -> Path: + if not value.is_file(): + raise errors.PathNotAFileError(path=value) + + return value + + class DirectoryPath(Path): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(format='directory-path') + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield path_validator + yield path_exists_validator + yield cls.validate + + @classmethod + def validate(cls, value: Path) -> Path: + if not value.is_dir(): + raise errors.PathNotADirectoryError(path=value) + + return value + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class JsonWrapper: + pass + + +class JsonMeta(type): + def __getitem__(self, t: Type[Any]) -> Type[JsonWrapper]: + if t is Any: + return Json # allow Json[Any] to replecate plain Json + return _registered(type('JsonWrapperValue', (JsonWrapper,), {'inner_type': t})) + + +if TYPE_CHECKING: + Json = Annotated[T, ...] # Json[list[str]] will be recognized by type checkers as list[str] + +else: + + class Json(metaclass=JsonMeta): + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + field_schema.update(type='string', format='json-string') + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class SecretField(abc.ABC): + """ + Note: this should be implemented as a generic like `SecretField(ABC, Generic[T])`, + the `__init__()` should be part of the abstract class and the + `get_secret_value()` method should use the generic `T` type. + + However Cython doesn't support very well generics at the moment and + the generated code fails to be imported (see + https://github.com/cython/cython/issues/2753). + """ + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() + + def __str__(self) -> str: + return '**********' if self.get_secret_value() else '' + + def __hash__(self) -> int: + return hash(self.get_secret_value()) + + @abc.abstractmethod + def get_secret_value(self) -> Any: # pragma: no cover + ... + + +class SecretStr(SecretField): + min_length: OptionalInt = None + max_length: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + type='string', + writeOnly=True, + format='password', + minLength=cls.min_length, + maxLength=cls.max_length, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + yield constr_length_validator + + @classmethod + def validate(cls, value: Any) -> 'SecretStr': + if isinstance(value, cls): + return value + value = str_validator(value) + return cls(value) + + def __init__(self, value: str): + self._secret_value = value + + def __repr__(self) -> str: + return f"SecretStr('{self}')" + + def __len__(self) -> int: + return len(self._secret_value) + + def display(self) -> str: + warnings.warn('`secret_str.display()` is deprecated, use `str(secret_str)` instead', DeprecationWarning) + return str(self) + + def get_secret_value(self) -> str: + return self._secret_value + + +class SecretBytes(SecretField): + min_length: OptionalInt = None + max_length: OptionalInt = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none( + field_schema, + type='string', + writeOnly=True, + format='password', + minLength=cls.min_length, + maxLength=cls.max_length, + ) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + yield constr_length_validator + + @classmethod + def validate(cls, value: Any) -> 'SecretBytes': + if isinstance(value, cls): + return value + value = bytes_validator(value) + return cls(value) + + def __init__(self, value: bytes): + self._secret_value = value + + def __repr__(self) -> str: + return f"SecretBytes(b'{self}')" + + def __len__(self) -> int: + return len(self._secret_value) + + def display(self) -> str: + warnings.warn('`secret_bytes.display()` is deprecated, use `str(secret_bytes)` instead', DeprecationWarning) + return str(self) + + def get_secret_value(self) -> bytes: + return self._secret_value + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class PaymentCardBrand(str, Enum): + # If you add another card type, please also add it to the + # Hypothesis strategy in `pydantic._hypothesis_plugin`. + amex = 'American Express' + mastercard = 'Mastercard' + visa = 'Visa' + other = 'other' + + def __str__(self) -> str: + return self.value + + +class PaymentCardNumber(str): + """ + Based on: https://en.wikipedia.org/wiki/Payment_card_number + """ + + strip_whitespace: ClassVar[bool] = True + min_length: ClassVar[int] = 12 + max_length: ClassVar[int] = 19 + bin: str + last4: str + brand: PaymentCardBrand + + def __init__(self, card_number: str): + self.bin = card_number[:6] + self.last4 = card_number[-4:] + self.brand = self._get_brand(card_number) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield str_validator + yield constr_strip_whitespace + yield constr_length_validator + yield cls.validate_digits + yield cls.validate_luhn_check_digit + yield cls + yield cls.validate_length_for_brand + + @property + def masked(self) -> str: + num_masked = len(self) - 10 # len(bin) + len(last4) == 10 + return f'{self.bin}{"*" * num_masked}{self.last4}' + + @classmethod + def validate_digits(cls, card_number: str) -> str: + if not card_number.isdigit(): + raise errors.NotDigitError + return card_number + + @classmethod + def validate_luhn_check_digit(cls, card_number: str) -> str: + """ + Based on: https://en.wikipedia.org/wiki/Luhn_algorithm + """ + sum_ = int(card_number[-1]) + length = len(card_number) + parity = length % 2 + for i in range(length - 1): + digit = int(card_number[i]) + if i % 2 == parity: + digit *= 2 + if digit > 9: + digit -= 9 + sum_ += digit + valid = sum_ % 10 == 0 + if not valid: + raise errors.LuhnValidationError + return card_number + + @classmethod + def validate_length_for_brand(cls, card_number: 'PaymentCardNumber') -> 'PaymentCardNumber': + """ + Validate length based on BIN for major brands: + https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN) + """ + required_length: Union[None, int, str] = None + if card_number.brand in PaymentCardBrand.mastercard: + required_length = 16 + valid = len(card_number) == required_length + elif card_number.brand == PaymentCardBrand.visa: + required_length = '13, 16 or 19' + valid = len(card_number) in {13, 16, 19} + elif card_number.brand == PaymentCardBrand.amex: + required_length = 15 + valid = len(card_number) == required_length + else: + valid = True + if not valid: + raise errors.InvalidLengthForBrand(brand=card_number.brand, required_length=required_length) + return card_number + + @staticmethod + def _get_brand(card_number: str) -> PaymentCardBrand: + if card_number[0] == '4': + brand = PaymentCardBrand.visa + elif 51 <= int(card_number[:2]) <= 55: + brand = PaymentCardBrand.mastercard + elif card_number[:2] in {'34', '37'}: + brand = PaymentCardBrand.amex + else: + brand = PaymentCardBrand.other + return brand + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +BYTE_SIZES = { + 'b': 1, + 'kb': 10**3, + 'mb': 10**6, + 'gb': 10**9, + 'tb': 10**12, + 'pb': 10**15, + 'eb': 10**18, + 'kib': 2**10, + 'mib': 2**20, + 'gib': 2**30, + 'tib': 2**40, + 'pib': 2**50, + 'eib': 2**60, +} +BYTE_SIZES.update({k.lower()[0]: v for k, v in BYTE_SIZES.items() if 'i' not in k}) +byte_string_re = re.compile(r'^\s*(\d*\.?\d+)\s*(\w+)?', re.IGNORECASE) + + +class ByteSize(int): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield cls.validate + + @classmethod + def validate(cls, v: StrIntFloat) -> 'ByteSize': + + try: + return cls(int(v)) + except ValueError: + pass + + str_match = byte_string_re.match(str(v)) + if str_match is None: + raise errors.InvalidByteSize() + + scalar, unit = str_match.groups() + if unit is None: + unit = 'b' + + try: + unit_mult = BYTE_SIZES[unit.lower()] + except KeyError: + raise errors.InvalidByteSizeUnit(unit=unit) + + return cls(int(float(scalar) * unit_mult)) + + def human_readable(self, decimal: bool = False) -> str: + + if decimal: + divisor = 1000 + units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] + final_unit = 'EB' + else: + divisor = 1024 + units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] + final_unit = 'EiB' + + num = float(self) + for unit in units: + if abs(num) < divisor: + return f'{num:0.1f}{unit}' + num /= divisor + + return f'{num:0.1f}{final_unit}' + + def to(self, unit: str) -> float: + + try: + unit_div = BYTE_SIZES[unit.lower()] + except KeyError: + raise errors.InvalidByteSizeUnit(unit=unit) + + return self / unit_div + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + PastDate = date + FutureDate = date +else: + + class PastDate(date): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield cls.validate + + @classmethod + def validate(cls, value: date) -> date: + if value >= date.today(): + raise errors.DateNotInThePastError() + + return value + + class FutureDate(date): + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield cls.validate + + @classmethod + def validate(cls, value: date) -> date: + if value <= date.today(): + raise errors.DateNotInTheFutureError() + + return value + + +class ConstrainedDate(date, metaclass=ConstrainedNumberMeta): + gt: OptionalDate = None + ge: OptionalDate = None + lt: OptionalDate = None + le: OptionalDate = None + + @classmethod + def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: + update_not_none(field_schema, exclusiveMinimum=cls.gt, exclusiveMaximum=cls.lt, minimum=cls.ge, maximum=cls.le) + + @classmethod + def __get_validators__(cls) -> 'CallableGenerator': + yield parse_date + yield number_size_validator + + +def condate( + *, + gt: date = None, + ge: date = None, + lt: date = None, + le: date = None, +) -> Type[date]: + # use kwargs then define conf in a dict to aid with IDE type hinting + namespace = dict(gt=gt, ge=ge, lt=lt, le=le) + return type('ConstrainedDateValue', (ConstrainedDate,), namespace) diff --git a/libs/win/pydantic/typing.cp37-win_amd64.pyd b/libs/win/pydantic/typing.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..d825e1a5baaf29b9d2ab0f24b4369435832d8f2e GIT binary patch literal 200704 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 Any: + return type_._evaluate(globalns, localns) + +else: + + def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: + # Even though it is the right signature for python 3.9, mypy complains with + # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... + return cast(Any, type_)._evaluate(globalns, localns, set()) + + +if sys.version_info < (3, 9): + # Ensure we always get all the whole `Annotated` hint, not just the annotated type. + # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`, + # so it already returns the full annotation + get_all_type_hints = get_type_hints + +else: + + def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any: + return get_type_hints(obj, globalns, localns, include_extras=True) + + +_T = TypeVar('_T') + +AnyCallable = TypingCallable[..., Any] +NoArgAnyCallable = TypingCallable[[], Any] + +# workaround for https://github.com/python/mypy/issues/9496 +AnyArgTCallable = TypingCallable[..., _T] + + +# Annotated[...] is implemented by returning an instance of one of these classes, depending on +# python/typing_extensions version. +AnnotatedTypeNames = {'AnnotatedMeta', '_AnnotatedAlias'} + + +if sys.version_info < (3, 8): + + def get_origin(t: Type[Any]) -> Optional[Type[Any]]: + if type(t).__name__ in AnnotatedTypeNames: + # weirdly this is a runtime requirement, as well as for mypy + return cast(Type[Any], Annotated) + return getattr(t, '__origin__', None) + +else: + from typing import get_origin as _typing_get_origin + + def get_origin(tp: Type[Any]) -> Optional[Type[Any]]: + """ + We can't directly use `typing.get_origin` since we need a fallback to support + custom generic classes like `ConstrainedList` + It should be useless once https://github.com/cython/cython/issues/3537 is + solved and https://github.com/pydantic/pydantic/pull/1753 is merged. + """ + if type(tp).__name__ in AnnotatedTypeNames: + return cast(Type[Any], Annotated) # mypy complains about _SpecialForm + return _typing_get_origin(tp) or getattr(tp, '__origin__', None) + + +if sys.version_info < (3, 8): + from typing import _GenericAlias + + def get_args(t: Type[Any]) -> Tuple[Any, ...]: + """Compatibility version of get_args for python 3.7. + + Mostly compatible with the python 3.8 `typing` module version + and able to handle almost all use cases. + """ + if type(t).__name__ in AnnotatedTypeNames: + return t.__args__ + t.__metadata__ + if isinstance(t, _GenericAlias): + res = t.__args__ + if t.__origin__ is Callable and res and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return getattr(t, '__args__', ()) + +else: + from typing import get_args as _typing_get_args + + def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """ + In python 3.9, `typing.Dict`, `typing.List`, ... + do have an empty `__args__` by default (instead of the generic ~T for example). + In order to still support `Dict` for example and consider it as `Dict[Any, Any]`, + we retrieve the `_nparams` value that tells us how many parameters it needs. + """ + if hasattr(tp, '_nparams'): + return (Any,) * tp._nparams + # Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple` + # in python 3.10- but now returns () for `tuple` and `Tuple`. + # This will probably be clarified in pydantic v2 + try: + if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]: # type: ignore[misc] + return ((),) + # there is a TypeError when compiled with cython + except TypeError: # pragma: no cover + pass + return () + + def get_args(tp: Type[Any]) -> Tuple[Any, ...]: + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if type(tp).__name__ in AnnotatedTypeNames: + return tp.__args__ + tp.__metadata__ + # the fallback is needed for the same reasons as `get_origin` (see above) + return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp) + + +if sys.version_info < (3, 9): + + def convert_generics(tp: Type[Any]) -> Type[Any]: + """Python 3.9 and older only supports generics from `typing` module. + They convert strings to ForwardRef automatically. + + Examples:: + typing.List['Hero'] == typing.List[ForwardRef('Hero')] + """ + return tp + +else: + from typing import _UnionGenericAlias # type: ignore + + from typing_extensions import _AnnotatedAlias + + def convert_generics(tp: Type[Any]) -> Type[Any]: + """ + Recursively searches for `str` type hints and replaces them with ForwardRef. + + Examples:: + convert_generics(list['Hero']) == list[ForwardRef('Hero')] + convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')] + convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')] + convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int + """ + origin = get_origin(tp) + if not origin or not hasattr(tp, '__args__'): + return tp + + args = get_args(tp) + + # typing.Annotated needs special treatment + if origin is Annotated: + return _AnnotatedAlias(convert_generics(args[0]), args[1:]) + + # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)` + converted = tuple( + ForwardRef(arg) if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) else convert_generics(arg) + for arg in args + ) + + if converted == args: + return tp + elif isinstance(tp, TypingGenericAlias): + return TypingGenericAlias(origin, converted) + elif isinstance(tp, TypesUnionType): + # recreate types.UnionType (PEP604, Python >= 3.10) + return _UnionGenericAlias(origin, converted) + else: + try: + setattr(tp, '__args__', converted) + except AttributeError: + pass + return tp + + +if sys.version_info < (3, 10): + + def is_union(tp: Optional[Type[Any]]) -> bool: + return tp is Union + + WithArgsTypes = (TypingGenericAlias,) + +else: + import types + import typing + + def is_union(tp: Optional[Type[Any]]) -> bool: + return tp is Union or tp is types.UnionType # noqa: E721 + + WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) + + +if sys.version_info < (3, 9): + StrPath = Union[str, PathLike] +else: + StrPath = Union[str, PathLike] + # TODO: Once we switch to Cython 3 to handle generics properly + # (https://github.com/cython/cython/issues/2753), use following lines instead + # of the one above + # # os.PathLike only becomes subscriptable from Python 3.9 onwards + # StrPath = Union[str, PathLike[str]] + + +if TYPE_CHECKING: + from .fields import ModelField + + TupleGenerator = Generator[Tuple[str, Any], None, None] + DictStrAny = Dict[str, Any] + DictAny = Dict[Any, Any] + SetStr = Set[str] + ListStr = List[str] + IntStr = Union[int, str] + AbstractSetIntStr = AbstractSet[IntStr] + DictIntStrAny = Dict[IntStr, Any] + MappingIntStrAny = Mapping[IntStr, Any] + CallableGenerator = Generator[AnyCallable, None, None] + ReprArgs = Sequence[Tuple[Optional[str], Any]] + AnyClassMethod = classmethod[Any] + +__all__ = ( + 'AnyCallable', + 'NoArgAnyCallable', + 'NoneType', + 'is_none_type', + 'display_as_type', + 'resolve_annotations', + 'is_callable_type', + 'is_literal_type', + 'all_literal_values', + 'is_namedtuple', + 'is_typeddict', + 'is_typeddict_special', + 'is_new_type', + 'new_type_supertype', + 'is_classvar', + 'is_finalvar', + 'update_field_forward_refs', + 'update_model_forward_refs', + 'TupleGenerator', + 'DictStrAny', + 'DictAny', + 'SetStr', + 'ListStr', + 'IntStr', + 'AbstractSetIntStr', + 'DictIntStrAny', + 'CallableGenerator', + 'ReprArgs', + 'AnyClassMethod', + 'CallableGenerator', + 'WithArgsTypes', + 'get_args', + 'get_origin', + 'get_sub_types', + 'typing_base', + 'get_all_type_hints', + 'is_union', + 'StrPath', + 'MappingIntStrAny', +) + + +NoneType = None.__class__ + + +NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None]) + + +if sys.version_info < (3, 8): + # Even though this implementation is slower, we need it for python 3.7: + # In python 3.7 "Literal" is not a builtin type and uses a different + # mechanism. + # for this reason `Literal[None] is Literal[None]` evaluates to `False`, + # breaking the faster implementation used for the other python versions. + + def is_none_type(type_: Any) -> bool: + return type_ in NONE_TYPES + +elif sys.version_info[:2] == (3, 8): + + def is_none_type(type_: Any) -> bool: + for none_type in NONE_TYPES: + if type_ is none_type: + return True + # With python 3.8, specifically 3.8.10, Literal "is" check sare very flakey + # can change on very subtle changes like use of types in other modules, + # hopefully this check avoids that issue. + if is_literal_type(type_): # pragma: no cover + return all_literal_values(type_) == (None,) + return False + +else: + + def is_none_type(type_: Any) -> bool: + for none_type in NONE_TYPES: + if type_ is none_type: + return True + return False + + +def display_as_type(v: Type[Any]) -> str: + if not isinstance(v, typing_base) and not isinstance(v, WithArgsTypes) and not isinstance(v, type): + v = v.__class__ + + if is_union(get_origin(v)): + return f'Union[{", ".join(map(display_as_type, get_args(v)))}]' + + if isinstance(v, WithArgsTypes): + # Generic alias are constructs like `list[int]` + return str(v).replace('typing.', '') + + try: + return v.__name__ + except AttributeError: + # happens with typing objects + return str(v).replace('typing.', '') + + +def resolve_annotations(raw_annotations: Dict[str, Type[Any]], module_name: Optional[str]) -> Dict[str, Type[Any]]: + """ + Partially taken from typing.get_type_hints. + + Resolve string or ForwardRef annotations into type objects if possible. + """ + base_globals: Optional[Dict[str, Any]] = None + if module_name: + try: + module = sys.modules[module_name] + except KeyError: + # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 + pass + else: + base_globals = module.__dict__ + + annotations = {} + for name, value in raw_annotations.items(): + if isinstance(value, str): + if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= (3, 10, 1): + value = ForwardRef(value, is_argument=False, is_class=True) + else: + value = ForwardRef(value, is_argument=False) + try: + value = _eval_type(value, base_globals, None) + except NameError: + # this is ok, it can be fixed with update_forward_refs + pass + annotations[name] = value + return annotations + + +def is_callable_type(type_: Type[Any]) -> bool: + return type_ is Callable or get_origin(type_) is Callable + + +def is_literal_type(type_: Type[Any]) -> bool: + return Literal is not None and get_origin(type_) is Literal + + +def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + return get_args(type_) + + +def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: + """ + This method is used to retrieve all Literal values as + Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) + e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]` + """ + if not is_literal_type(type_): + return (type_,) + + values = literal_values(type_) + return tuple(x for value in values for x in all_literal_values(value)) + + +def is_namedtuple(type_: Type[Any]) -> bool: + """ + Check if a given class is a named tuple. + It can be either a `typing.NamedTuple` or `collections.namedtuple` + """ + from .utils import lenient_issubclass + + return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') + + +def is_typeddict(type_: Type[Any]) -> bool: + """ + Check if a given class is a typed dict (from `typing` or `typing_extensions`) + In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict) + """ + from .utils import lenient_issubclass + + return lenient_issubclass(type_, dict) and hasattr(type_, '__total__') + + +def _check_typeddict_special(type_: Any) -> bool: + return type_ is TypedDictRequired or type_ is TypedDictNotRequired + + +def is_typeddict_special(type_: Any) -> bool: + """ + Check if type is a TypedDict special form (Required or NotRequired). + """ + return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_)) + + +test_type = NewType('test_type', str) + + +def is_new_type(type_: Type[Any]) -> bool: + """ + Check whether type_ was created using typing.NewType + """ + return isinstance(type_, test_type.__class__) and hasattr(type_, '__supertype__') # type: ignore + + +def new_type_supertype(type_: Type[Any]) -> Type[Any]: + while hasattr(type_, '__supertype__'): + type_ = type_.__supertype__ + return type_ + + +def _check_classvar(v: Optional[Type[Any]]) -> bool: + if v is None: + return False + + return v.__class__ == ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' + + +def _check_finalvar(v: Optional[Type[Any]]) -> bool: + """ + Check if a given type is a `typing.Final` type. + """ + if v is None: + return False + + return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') + + +def is_classvar(ann_type: Type[Any]) -> bool: + if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): + return True + + # this is an ugly workaround for class vars that contain forward references and are therefore themselves + # forward references, see #3679 + if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith('ClassVar['): + return True + + return False + + +def is_finalvar(ann_type: Type[Any]) -> bool: + return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) + + +def update_field_forward_refs(field: 'ModelField', globalns: Any, localns: Any) -> None: + """ + Try to update ForwardRefs on fields based on this ModelField, globalns and localns. + """ + prepare = False + if field.type_.__class__ == ForwardRef: + prepare = True + field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) + if field.outer_type_.__class__ == ForwardRef: + prepare = True + field.outer_type_ = evaluate_forwardref(field.outer_type_, globalns, localns or None) + if prepare: + field.prepare() + + if field.sub_fields: + for sub_f in field.sub_fields: + update_field_forward_refs(sub_f, globalns=globalns, localns=localns) + + if field.discriminator_key is not None: + field.prepare_discriminated_union_sub_fields() + + +def update_model_forward_refs( + model: Type[Any], + fields: Iterable['ModelField'], + json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable], + localns: 'DictStrAny', + exc_to_suppress: Tuple[Type[BaseException], ...] = (), +) -> None: + """ + Try to update model fields ForwardRefs based on model and localns. + """ + if model.__module__ in sys.modules: + globalns = sys.modules[model.__module__].__dict__.copy() + else: + globalns = {} + + globalns.setdefault(model.__name__, model) + + for f in fields: + try: + update_field_forward_refs(f, globalns=globalns, localns=localns) + except exc_to_suppress: + pass + + for key in set(json_encoders.keys()): + if isinstance(key, str): + fr: ForwardRef = ForwardRef(key) + elif isinstance(key, ForwardRef): + fr = key + else: + continue + + try: + new_key = evaluate_forwardref(fr, globalns, localns or None) + except exc_to_suppress: # pragma: no cover + continue + + json_encoders[new_key] = json_encoders.pop(key) + + +def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]: + """ + Tries to get the class of a Type[T] annotation. Returns True if Type is used + without brackets. Otherwise returns None. + """ + if type_ is type: + return True + + if get_origin(type_) is None: + return None + + args = get_args(type_) + if not args or not isinstance(args[0], type): + return True + else: + return args[0] + + +def get_sub_types(tp: Any) -> List[Any]: + """ + Return all the types that are allowed by type `tp` + `tp` can be a `Union` of allowed types or an `Annotated` type + """ + origin = get_origin(tp) + if origin is Annotated: + return get_sub_types(get_args(tp)[0]) + elif is_union(origin): + return [x for t in get_args(tp) for x in get_sub_types(t)] + else: + return [tp] diff --git a/libs/win/pydantic/utils.cp37-win_amd64.pyd b/libs/win/pydantic/utils.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..e3bfb22c8255f3ce13b40b9f2af04f491065a80b GIT binary patch literal 305152 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%0ALM^;dWa0J2Lo|Oj`omx#_ZGGRrxfif}UCgc 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 literal 0 HcmV?d00001 diff --git a/libs/win/pydantic/utils.py b/libs/win/pydantic/utils.py new file mode 100644 index 00000000..1d016c0e --- /dev/null +++ b/libs/win/pydantic/utils.py @@ -0,0 +1,841 @@ +import keyword +import warnings +import weakref +from collections import OrderedDict, defaultdict, deque +from copy import deepcopy +from itertools import islice, zip_longest +from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType +from typing import ( + TYPE_CHECKING, + AbstractSet, + Any, + Callable, + Collection, + Dict, + Generator, + Iterable, + Iterator, + List, + Mapping, + MutableMapping, + NoReturn, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, +) + +from typing_extensions import Annotated + +from .errors import ConfigError +from .typing import ( + NoneType, + WithArgsTypes, + all_literal_values, + display_as_type, + get_args, + get_origin, + is_literal_type, + is_union, +) +from .version import version_info + +if TYPE_CHECKING: + from inspect import Signature + from pathlib import Path + + from .config import BaseConfig + from .dataclasses import Dataclass + from .fields import ModelField + from .main import BaseModel + from .typing import AbstractSetIntStr, DictIntStrAny, IntStr, MappingIntStrAny, ReprArgs + + RichReprResult = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]] + +__all__ = ( + 'import_string', + 'sequence_like', + 'validate_field_name', + 'lenient_isinstance', + 'lenient_issubclass', + 'in_ipython', + 'is_valid_identifier', + 'deep_update', + 'update_not_none', + 'almost_equal_floats', + 'get_model', + 'to_camel', + 'is_valid_field', + 'smart_deepcopy', + 'PyObjectStr', + 'Representation', + 'GetterDict', + 'ValueItems', + 'version_info', # required here to match behaviour in v1.3 + 'ClassAttribute', + 'path_type', + 'ROOT_KEY', + 'get_unique_discriminator_alias', + 'get_discriminator_alias_and_values', + 'DUNDER_ATTRIBUTES', + 'LimitedDict', +) + +ROOT_KEY = '__root__' +# these are types that are returned unchanged by deepcopy +IMMUTABLE_NON_COLLECTIONS_TYPES: Set[Type[Any]] = { + int, + float, + complex, + str, + bool, + bytes, + type, + NoneType, + FunctionType, + BuiltinFunctionType, + LambdaType, + weakref.ref, + CodeType, + # note: including ModuleType will differ from behaviour of deepcopy by not producing error. + # It might be not a good idea in general, but considering that this function used only internally + # against default values of fields, this will allow to actually have a field with module as default value + ModuleType, + NotImplemented.__class__, + Ellipsis.__class__, +} + +# these are types that if empty, might be copied with simple copy() instead of deepcopy() +BUILTIN_COLLECTIONS: Set[Type[Any]] = { + list, + set, + tuple, + frozenset, + dict, + OrderedDict, + defaultdict, + deque, +} + + +def import_string(dotted_path: str) -> Any: + """ + Stolen approximately from django. Import a dotted module path and return the attribute/class designated by the + last name in the path. Raise ImportError if the import fails. + """ + from importlib import import_module + + try: + module_path, class_name = dotted_path.strip(' ').rsplit('.', 1) + except ValueError as e: + raise ImportError(f'"{dotted_path}" doesn\'t look like a module path') from e + + module = import_module(module_path) + try: + return getattr(module, class_name) + except AttributeError as e: + raise ImportError(f'Module "{module_path}" does not define a "{class_name}" attribute') from e + + +def truncate(v: Union[str], *, max_len: int = 80) -> str: + """ + Truncate a value and add a unicode ellipsis (three dots) to the end if it was too long + """ + warnings.warn('`truncate` is no-longer used by pydantic and is deprecated', DeprecationWarning) + if isinstance(v, str) and len(v) > (max_len - 2): + # -3 so quote + string + … + quote has correct length + return (v[: (max_len - 3)] + '…').__repr__() + try: + v = v.__repr__() + except TypeError: + v = v.__class__.__repr__(v) # in case v is a type + if len(v) > max_len: + v = v[: max_len - 1] + '…' + return v + + +def sequence_like(v: Any) -> bool: + return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque)) + + +def validate_field_name(bases: List[Type['BaseModel']], field_name: str) -> None: + """ + Ensure that the field's name does not shadow an existing attribute of the model. + """ + for base in bases: + if getattr(base, field_name, None): + raise NameError( + f'Field name "{field_name}" shadows a BaseModel attribute; ' + f'use a different field name with "alias=\'{field_name}\'".' + ) + + +def lenient_isinstance(o: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool: + try: + return isinstance(o, class_or_tuple) # type: ignore[arg-type] + except TypeError: + return False + + +def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool: + try: + return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type] + except TypeError: + if isinstance(cls, WithArgsTypes): + return False + raise # pragma: no cover + + +def in_ipython() -> bool: + """ + Check whether we're in an ipython environment, including jupyter notebooks. + """ + try: + eval('__IPYTHON__') + except NameError: + return False + else: # pragma: no cover + return True + + +def is_valid_identifier(identifier: str) -> bool: + """ + Checks that a string is a valid identifier and not a Python keyword. + :param identifier: The identifier to test. + :return: True if the identifier is valid. + """ + return identifier.isidentifier() and not keyword.iskeyword(identifier) + + +KeyType = TypeVar('KeyType') + + +def deep_update(mapping: Dict[KeyType, Any], *updating_mappings: Dict[KeyType, Any]) -> Dict[KeyType, Any]: + updated_mapping = mapping.copy() + for updating_mapping in updating_mappings: + for k, v in updating_mapping.items(): + if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict): + updated_mapping[k] = deep_update(updated_mapping[k], v) + else: + updated_mapping[k] = v + return updated_mapping + + +def update_not_none(mapping: Dict[Any, Any], **update: Any) -> None: + mapping.update({k: v for k, v in update.items() if v is not None}) + + +def almost_equal_floats(value_1: float, value_2: float, *, delta: float = 1e-8) -> bool: + """ + Return True if two floats are almost equal + """ + return abs(value_1 - value_2) <= delta + + +def generate_model_signature( + init: Callable[..., None], fields: Dict[str, 'ModelField'], config: Type['BaseConfig'] +) -> 'Signature': + """ + Generate signature for model based on its fields + """ + from inspect import Parameter, Signature, signature + + from .config import Extra + + present_params = signature(init).parameters.values() + merged_params: Dict[str, Parameter] = {} + var_kw = None + use_var_kw = False + + for param in islice(present_params, 1, None): # skip self arg + if param.kind is param.VAR_KEYWORD: + var_kw = param + continue + merged_params[param.name] = param + + if var_kw: # if custom init has no var_kw, fields which are not declared in it cannot be passed through + allow_names = config.allow_population_by_field_name + for field_name, field in fields.items(): + param_name = field.alias + if field_name in merged_params or param_name in merged_params: + continue + elif not is_valid_identifier(param_name): + if allow_names and is_valid_identifier(field_name): + param_name = field_name + else: + use_var_kw = True + continue + + # TODO: replace annotation with actual expected types once #1055 solved + kwargs = {'default': field.default} if not field.required else {} + merged_params[param_name] = Parameter( + param_name, Parameter.KEYWORD_ONLY, annotation=field.annotation, **kwargs + ) + + if config.extra is Extra.allow: + use_var_kw = True + + if var_kw and use_var_kw: + # Make sure the parameter for extra kwargs + # does not have the same name as a field + default_model_signature = [ + ('__pydantic_self__', Parameter.POSITIONAL_OR_KEYWORD), + ('data', Parameter.VAR_KEYWORD), + ] + if [(p.name, p.kind) for p in present_params] == default_model_signature: + # if this is the standard model signature, use extra_data as the extra args name + var_kw_name = 'extra_data' + else: + # else start from var_kw + var_kw_name = var_kw.name + + # generate a name that's definitely unique + while var_kw_name in fields: + var_kw_name += '_' + merged_params[var_kw_name] = var_kw.replace(name=var_kw_name) + + return Signature(parameters=list(merged_params.values()), return_annotation=None) + + +def get_model(obj: Union[Type['BaseModel'], Type['Dataclass']]) -> Type['BaseModel']: + from .main import BaseModel + + try: + model_cls = obj.__pydantic_model__ # type: ignore + except AttributeError: + model_cls = obj + + if not issubclass(model_cls, BaseModel): + raise TypeError('Unsupported type, must be either BaseModel or dataclass') + return model_cls + + +def to_camel(string: str) -> str: + return ''.join(word.capitalize() for word in string.split('_')) + + +def to_lower_camel(string: str) -> str: + if len(string) >= 1: + pascal_string = to_camel(string) + return pascal_string[0].lower() + pascal_string[1:] + return string.lower() + + +T = TypeVar('T') + + +def unique_list( + input_list: Union[List[T], Tuple[T, ...]], + *, + name_factory: Callable[[T], str] = str, +) -> List[T]: + """ + Make a list unique while maintaining order. + We update the list if another one with the same name is set + (e.g. root validator overridden in subclass) + """ + result: List[T] = [] + result_names: List[str] = [] + for v in input_list: + v_name = name_factory(v) + if v_name not in result_names: + result_names.append(v_name) + result.append(v) + else: + result[result_names.index(v_name)] = v + + return result + + +class PyObjectStr(str): + """ + String class where repr doesn't include quotes. Useful with Representation when you want to return a string + representation of something that valid (or pseudo-valid) python. + """ + + def __repr__(self) -> str: + return str(self) + + +class Representation: + """ + Mixin to provide __str__, __repr__, and __pretty__ methods. See #884 for more details. + + __pretty__ is used by [devtools](https://python-devtools.helpmanual.io/) to provide human readable representations + of objects. + """ + + __slots__: Tuple[str, ...] = tuple() + + def __repr_args__(self) -> 'ReprArgs': + """ + Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden. + + Can either return: + * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]` + * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]` + """ + attrs = ((s, getattr(self, s)) for s in self.__slots__) + return [(a, v) for a, v in attrs if v is not None] + + def __repr_name__(self) -> str: + """ + Name of the instance's class, used in __repr__. + """ + return self.__class__.__name__ + + def __repr_str__(self, join_str: str) -> str: + return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__()) + + def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any, None, None]: + """ + Used by devtools (https://python-devtools.helpmanual.io/) to provide a human readable representations of objects + """ + yield self.__repr_name__() + '(' + yield 1 + for name, value in self.__repr_args__(): + if name is not None: + yield name + '=' + yield fmt(value) + yield ',' + yield 0 + yield -1 + yield ')' + + def __str__(self) -> str: + return self.__repr_str__(' ') + + def __repr__(self) -> str: + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' + + def __rich_repr__(self) -> 'RichReprResult': + """Get fields for Rich library""" + for name, field_repr in self.__repr_args__(): + if name is None: + yield field_repr + else: + yield name, field_repr + + +class GetterDict(Representation): + """ + Hack to make object's smell just enough like dicts for validate_model. + + We can't inherit from Mapping[str, Any] because it upsets cython so we have to implement all methods ourselves. + """ + + __slots__ = ('_obj',) + + def __init__(self, obj: Any): + self._obj = obj + + def __getitem__(self, key: str) -> Any: + try: + return getattr(self._obj, key) + except AttributeError as e: + raise KeyError(key) from e + + def get(self, key: Any, default: Any = None) -> Any: + return getattr(self._obj, key, default) + + def extra_keys(self) -> Set[Any]: + """ + We don't want to get any other attributes of obj if the model didn't explicitly ask for them + """ + return set() + + def keys(self) -> List[Any]: + """ + Keys of the pseudo dictionary, uses a list not set so order information can be maintained like python + dictionaries. + """ + return list(self) + + def values(self) -> List[Any]: + return [self[k] for k in self] + + def items(self) -> Iterator[Tuple[str, Any]]: + for k in self: + yield k, self.get(k) + + def __iter__(self) -> Iterator[str]: + for name in dir(self._obj): + if not name.startswith('_'): + yield name + + def __len__(self) -> int: + return sum(1 for _ in self) + + def __contains__(self, item: Any) -> bool: + return item in self.keys() + + def __eq__(self, other: Any) -> bool: + return dict(self) == dict(other.items()) + + def __repr_args__(self) -> 'ReprArgs': + return [(None, dict(self))] + + def __repr_name__(self) -> str: + return f'GetterDict[{display_as_type(self._obj)}]' + + +class ValueItems(Representation): + """ + Class for more convenient calculation of excluded or included fields on values. + """ + + __slots__ = ('_items', '_type') + + def __init__(self, value: Any, items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> None: + items = self._coerce_items(items) + + if isinstance(value, (list, tuple)): + items = self._normalize_indexes(items, len(value)) + + self._items: 'MappingIntStrAny' = items + + def is_excluded(self, item: Any) -> bool: + """ + Check if item is fully excluded. + + :param item: key or index of a value + """ + return self.is_true(self._items.get(item)) + + def is_included(self, item: Any) -> bool: + """ + Check if value is contained in self._items + + :param item: key or index of value + """ + return item in self._items + + def for_element(self, e: 'IntStr') -> Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']]: + """ + :param e: key or index of element on value + :return: raw values for element if self._items is dict and contain needed element + """ + + item = self._items.get(e) + return item if not self.is_true(item) else None + + def _normalize_indexes(self, items: 'MappingIntStrAny', v_length: int) -> 'DictIntStrAny': + """ + :param items: dict or set of indexes which will be normalized + :param v_length: length of sequence indexes of which will be + + >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4) + {0: True, 2: True, 3: True} + >>> self._normalize_indexes({'__all__': True}, 4) + {0: True, 1: True, 2: True, 3: True} + """ + + normalized_items: 'DictIntStrAny' = {} + all_items = None + for i, v in items.items(): + if not (isinstance(v, Mapping) or isinstance(v, AbstractSet) or self.is_true(v)): + raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}') + if i == '__all__': + all_items = self._coerce_value(v) + continue + if not isinstance(i, int): + raise TypeError( + 'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: ' + 'expected integer keys or keyword "__all__"' + ) + normalized_i = v_length + i if i < 0 else i + normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i)) + + if not all_items: + return normalized_items + if self.is_true(all_items): + for i in range(v_length): + normalized_items.setdefault(i, ...) + return normalized_items + for i in range(v_length): + normalized_item = normalized_items.setdefault(i, {}) + if not self.is_true(normalized_item): + normalized_items[i] = self.merge(all_items, normalized_item) + return normalized_items + + @classmethod + def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any: + """ + Merge a ``base`` item with an ``override`` item. + + Both ``base`` and ``override`` are converted to dictionaries if possible. + Sets are converted to dictionaries with the sets entries as keys and + Ellipsis as values. + + Each key-value pair existing in ``base`` is merged with ``override``, + while the rest of the key-value pairs are updated recursively with this function. + + Merging takes place based on the "union" of keys if ``intersect`` is + set to ``False`` (default) and on the intersection of keys if + ``intersect`` is set to ``True``. + """ + override = cls._coerce_value(override) + base = cls._coerce_value(base) + if override is None: + return base + if cls.is_true(base) or base is None: + return override + if cls.is_true(override): + return base if intersect else override + + # intersection or union of keys while preserving ordering: + if intersect: + merge_keys = [k for k in base if k in override] + [k for k in override if k in base] + else: + merge_keys = list(base) + [k for k in override if k not in base] + + merged: 'DictIntStrAny' = {} + for k in merge_keys: + merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect) + if merged_item is not None: + merged[k] = merged_item + + return merged + + @staticmethod + def _coerce_items(items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> 'MappingIntStrAny': + if isinstance(items, Mapping): + pass + elif isinstance(items, AbstractSet): + items = dict.fromkeys(items, ...) + else: + class_name = getattr(items, '__class__', '???') + assert_never( + items, + f'Unexpected type of exclude value {class_name}', + ) + return items + + @classmethod + def _coerce_value(cls, value: Any) -> Any: + if value is None or cls.is_true(value): + return value + return cls._coerce_items(value) + + @staticmethod + def is_true(v: Any) -> bool: + return v is True or v is ... + + def __repr_args__(self) -> 'ReprArgs': + return [(None, self._items)] + + +class ClassAttribute: + """ + Hide class attribute from its instances + """ + + __slots__ = ( + 'name', + 'value', + ) + + def __init__(self, name: str, value: Any) -> None: + self.name = name + self.value = value + + def __get__(self, instance: Any, owner: Type[Any]) -> None: + if instance is None: + return self.value + raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only') + + +path_types = { + 'is_dir': 'directory', + 'is_file': 'file', + 'is_mount': 'mount point', + 'is_symlink': 'symlink', + 'is_block_device': 'block device', + 'is_char_device': 'char device', + 'is_fifo': 'FIFO', + 'is_socket': 'socket', +} + + +def path_type(p: 'Path') -> str: + """ + Find out what sort of thing a path is. + """ + assert p.exists(), 'path does not exist' + for method, name in path_types.items(): + if getattr(p, method)(): + return name + + return 'unknown' + + +Obj = TypeVar('Obj') + + +def smart_deepcopy(obj: Obj) -> Obj: + """ + Return type as is for immutable built-in types + Use obj.copy() for built-in empty collections + Use copy.deepcopy() for non-empty collections and unknown objects + """ + + obj_type = obj.__class__ + if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES: + return obj # fastest case: obj is immutable and not collection therefore will not be copied anyway + try: + if not obj and obj_type in BUILTIN_COLLECTIONS: + # faster way for empty collections, no need to copy its members + return obj if obj_type is tuple else obj.copy() # type: ignore # tuple doesn't have copy method + except (TypeError, ValueError, RuntimeError): + # do we really dare to catch ALL errors? Seems a bit risky + pass + + return deepcopy(obj) # slowest way when we actually might need a deepcopy + + +def is_valid_field(name: str) -> bool: + if not name.startswith('_'): + return True + return ROOT_KEY == name + + +DUNDER_ATTRIBUTES = { + '__annotations__', + '__classcell__', + '__doc__', + '__module__', + '__orig_bases__', + '__orig_class__', + '__qualname__', +} + + +def is_valid_private_name(name: str) -> bool: + return not is_valid_field(name) and name not in DUNDER_ATTRIBUTES + + +_EMPTY = object() + + +def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool: + """ + Check that the items of `left` are the same objects as those in `right`. + + >>> a, b = object(), object() + >>> all_identical([a, b, a], [a, b, a]) + True + >>> all_identical([a, b, [a]], [a, b, [a]]) # new list object, while "equal" is not "identical" + False + """ + for left_item, right_item in zip_longest(left, right, fillvalue=_EMPTY): + if left_item is not right_item: + return False + return True + + +def assert_never(obj: NoReturn, msg: str) -> NoReturn: + """ + Helper to make sure that we have covered all possible types. + + This is mostly useful for ``mypy``, docs: + https://mypy.readthedocs.io/en/latest/literal_types.html#exhaustive-checks + """ + raise TypeError(msg) + + +def get_unique_discriminator_alias(all_aliases: Collection[str], discriminator_key: str) -> str: + """Validate that all aliases are the same and if that's the case return the alias""" + unique_aliases = set(all_aliases) + if len(unique_aliases) > 1: + raise ConfigError( + f'Aliases for discriminator {discriminator_key!r} must be the same (got {", ".join(sorted(all_aliases))})' + ) + return unique_aliases.pop() + + +def get_discriminator_alias_and_values(tp: Any, discriminator_key: str) -> Tuple[str, Tuple[str, ...]]: + """ + Get alias and all valid values in the `Literal` type of the discriminator field + `tp` can be a `BaseModel` class or directly an `Annotated` `Union` of many. + """ + is_root_model = getattr(tp, '__custom_root_type__', False) + + if get_origin(tp) is Annotated: + tp = get_args(tp)[0] + + if hasattr(tp, '__pydantic_model__'): + tp = tp.__pydantic_model__ + + if is_union(get_origin(tp)): + alias, all_values = _get_union_alias_and_all_values(tp, discriminator_key) + return alias, tuple(v for values in all_values for v in values) + elif is_root_model: + union_type = tp.__fields__[ROOT_KEY].type_ + alias, all_values = _get_union_alias_and_all_values(union_type, discriminator_key) + + if len(set(all_values)) > 1: + raise ConfigError( + f'Field {discriminator_key!r} is not the same for all submodels of {display_as_type(tp)!r}' + ) + + return alias, all_values[0] + + else: + try: + t_discriminator_type = tp.__fields__[discriminator_key].type_ + except AttributeError as e: + raise TypeError(f'Type {tp.__name__!r} is not a valid `BaseModel` or `dataclass`') from e + except KeyError as e: + raise ConfigError(f'Model {tp.__name__!r} needs a discriminator field for key {discriminator_key!r}') from e + + if not is_literal_type(t_discriminator_type): + raise ConfigError(f'Field {discriminator_key!r} of model {tp.__name__!r} needs to be a `Literal`') + + return tp.__fields__[discriminator_key].alias, all_literal_values(t_discriminator_type) + + +def _get_union_alias_and_all_values( + union_type: Type[Any], discriminator_key: str +) -> Tuple[str, Tuple[Tuple[str, ...], ...]]: + zipped_aliases_values = [get_discriminator_alias_and_values(t, discriminator_key) for t in get_args(union_type)] + # unzip: [('alias_a',('v1', 'v2)), ('alias_b', ('v3',))] => [('alias_a', 'alias_b'), (('v1', 'v2'), ('v3',))] + all_aliases, all_values = zip(*zipped_aliases_values) + return get_unique_discriminator_alias(all_aliases, discriminator_key), all_values + + +KT = TypeVar('KT') +VT = TypeVar('VT') +if TYPE_CHECKING: + # Annoying inheriting from `MutableMapping` and `dict` breaks cython, hence this work around + class LimitedDict(dict, MutableMapping[KT, VT]): # type: ignore[type-arg] + def __init__(self, size_limit: int = 1000): + ... + +else: + + class LimitedDict(dict): + """ + Limit the size/length of a dict used for caching to avoid unlimited increase in memory usage. + + Since the dict is ordered, and we always remove elements from the beginning, this is effectively a FIFO cache. + + Annoying inheriting from `MutableMapping` breaks cython. + """ + + def __init__(self, size_limit: int = 1000): + self.size_limit = size_limit + super().__init__() + + def __setitem__(self, __key: Any, __value: Any) -> None: + super().__setitem__(__key, __value) + if len(self) > self.size_limit: + excess = len(self) - self.size_limit + self.size_limit // 10 + to_remove = list(self.keys())[:excess] + for key in to_remove: + del self[key] + + def __class_getitem__(cls, *args: Any) -> Any: + # to avoid errors with 3.7 + pass diff --git a/libs/win/pydantic/validators.cp37-win_amd64.pyd b/libs/win/pydantic/validators.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..a59f4be20c23c717773bbbbcd88e2767bae364a4 GIT binary patch literal 265216 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�ZYECdx%(_dMu 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

  • |CFeUIX!B)wG_i0C%7qw{s}HnBCdJof+EFkQ zfs;Kj#IeMV`nW<*IKymn7n^{uO&xfQu`1&dto;QxeUS(ZtUCK%8(D}dcl9mn#nUkBmp}Q+jxL?mq2( zdAt{obDt5E4?RgA_`ZeSXmB-A(^eK2l^=!5-+oS~`J@vEYLlyYv?9LG!c^OuO&tkx z#5*?dsGwkvwhu6bd*ZAyd9QjQ;z;BDj#i1* zFbQ|QWFz_jXW_!ew&9vh71d|VQKy?O2f-UeqMQxq>d2{i&(x7m7?*VDR=sg^Hcxn+ z=pZimnCXf3fw*YT$)G*QL;KCy=sa@=Bd5ljWH*-Q& zyJd4Uq8RfiIYfGVl!nOqekfPb#K69(i%!en<`Z}q;T8e6e4hmflkbs_gY4=PfE>f8 zIUHUNi*@7}EB6198Es-xXcdcJSS(gHFZEr2+G@q*;X1M+5B0Sw_4@Hb$2x7?-Qt^{2DAm-16>hV+W)_|6jl=;0at=Ve`!B^_C+U6z zvLh>xajA^799C8!_JEbAK$>#!5emDFLb zq+Sh*uMt(v@5&>2&(xK2MkF2jL~jxbgDZX=f7B!A^Xke`*uwuyX_cWj`J}I$X|({o z68NM)G0}F*5Mue&*6~+)M92H4UOc54Za$eCgxeqVB^KF59ADoT#=}5$2423?msn1F z^dz?OKS^vQHBDp(U?P4^WTVb264`xmG(3@YDrN2?dAsr@#pnqb<8@=swZb^{-G36> zM#0VV{w209kkVo^IOpo4D5jO%+ zs{4hu7;6MdJS9&>$`lt-b4U)DLNeaha{i|Qspb5$?eDD81^m^~RciXbb)^XFZnxsC z2jK`|cn7Jqbj=81w9<-0GP1R@t(T;Wd-vd6Iv z?3+_A>XnPA$C=~UI%y7&3><1sQS;;2wsw-llL1d{W3d<6lO5;0T?}hHWTVWQegDvg zvjjOAC{bu9XD@mu-GdnCy9HLs^+<*SUXc<_Ml4Kr5uKd#CSbke*wB@B9NS8ZZWR7> z8#+B1NUFLbV8B?YH+-L7e*_&cYSln;j;P>m1|BQsIzF$Gg`=q)p8X*$raosr!M@e~ zbIxmMgY36%jWv*i|A31Q)xF< zdXP$N{z_DCh$L?4ORBZzhLVEnlbH&qL+8?dq+jK_U>$3Uz@%z{0$<6>*^%T>@>-Ac zS~JRCyu<{~Hrr6S02Se0j#$vB>5R=zlM`grAEHKl2pXejw0tm`$L}N$$uKFp%AUlS z*P>H!adWdH@oM%F)8x}M**7)kRCVSC+<_5K%L!2&qG=LTebdGo^Q>F2jDQ3s+O#CaCIY(?Ky!>Mcg`nE*N0nX+@vF3I;JP4G|~E%y_! z+i1jcZAba2wYUE6vqs0-pFwWEAYQK6%plR+o7;EeY0QD>DxMBarjsB$<6Ci6-UQeJ ze(%|qRVOk=O_D>wkwUy#fMWI6HQ|ivDphlm+7M3!Yd^CQBA?0^VfHrCxUsaw)`%PD z^1s!t?!2UfoZuA0=wB`e%&4=%&8NWmj=GqC#M6y`Vv-bv8!XqhL&PC!pf9~DciKo^ z61)marR$QR&mR$%@OyiR)aVA_KcS)U{Jvt`<+^Ne9~`9zFW7@mt;A`1@MzA1!Fq5{ z&VwpFP+lwC@Uc}nga=LIBjtsazk`?d1dp3Vj$~*lYRB-H%!{T7FaE*>PsAZN^x*$C zsGy0k9Oug@ZWv=p8ui{F!^k{Km39rg5wm0{We>0Cp)a~!X;=IEdW?EXP^$=a<#SW( zNvZ!j7@^NGB@4CRs#4!2dL#Aq2q}9KpD#wKdoNg50EWt6fNoB&?} zdK{~f(mwrhcDRPleg8P-{1D+&s1#?NrWQQYW4bq|S6QZeU zzoNO{2)l-7H1MTtbp(M$eJsf(zfykfVDv=bNuEV4$MyP=Nin%X zpt>@+Ta^eUHPz`X{Y@DwJT-00EEBet$9pHxZy*9NB^KUCCb>!tXqv-2G z^aPne|Ue%osz1N@^uM`H{(!H%i|b%nt-}ign(W`9Z3y0M$z6zf#%T&n>!l ze;m5IFg_BDeOtvV5V;BZa4>)!2^w`q6e}Cds+${#8F029TNby-zs*@_Y^fpoe|VNY zd*sju-MTt;j3t&*J`dbU{?EhZE8%hkxU{l49o2qnwi88Ojc-+Bvn8MMMi?z$Qk9Gs zRlx%+fx#>5%Q~1T2e6Y!#~M@>v+5;%O0xY@CSXjq`$dwc@`eFW3R z$^*meP;_Q={s6bQMkn6BEUiyYg$5rkYO_mYb!v&iF~$=1QQQnmu){9YlVU3KpoMH0 zLhZoWZbR61FFG(+9pD_$-V(IW#eb5{O3w68^*=%8j4vI0Z^b*SxIXI&2E*d%_UpYE1{UaxR#M`?6AjEkxW`%dvjGFt{N{H@EZPN1*BvP9uh#G0ct z?lwp{M``>CmXJxTQy4>`p_x70+B!<(OZ);aw9%_pk3DYH&bx5Ke!_h}rMUPG8wli_ zDS5o`N$A5V@Ns!yfoM(yk~MbUHI+k|WkACO=7$6BRU{3N7$-pN=E75 z#WvRak;vD)NTgBSi$u2CRXb0Uj`ogDU+_2sM}IdkJXP&pvY3ZyM?GBZj#=N-+X@c4 zQ7qAVF&>FW>k47GR7Wwj(0PH+kTcv>(=E6Qu5KaPd;^r0Zi%$eEf$VM{jN=oK8U)= z8XB#+`n4P*-y=K3)pmLL25h9ALHvWU;}9Wtry-Z8IxGY!c`Z2o(TV9?Ov*aE39DQvfscygeCb5Iq_c3+#C--nFtr^ zO-6+K{BndBoa`?VX1_wWBe}?k@B<a#gi~ptOjbb%P@G5fV zlHj-Zbt@|aJ=>lI22baae?BH>nwsKgaEQe#y2aqI*0Gu5_if|a)XD>?`>VEbP3kcX zo(`RLU4XkDhF#26QYJzwHhR_^mgJ!4~q{hJ!? z*Et%Db&e#@PGcXZcF_douhYbVn}85MO(Am9#&>2PNla$W%JjQV{k~rrUq|kl@N!No zj?^sb6gh--aP4=D;AthJ3NS}%n%cxaBY2k%l>^TlIL{^t=%6gnn*=lp(3T-~2ypdO zDzk?OS5XJdaNO3RG3Qdw{Q0jexs8W@%kEccVu@L$(|8(7%=1nKn(3Z`fvV;MH5|vaLdS6p zZtcT!LM~nuZg?nz*w@HsbFV%pWAU;0T}cYx>g#aS5;kQk+R}IjNQ9H8wBsD68{8pd z7qw>@3yksqrm?!+Yk?K2BNYqL5iAw>lP)a{B00IL#DHbIUBFb91G<)#L=(f8_4Haw zjm%IM|H1|RfF0w0x?DtYZE&O#R zU&LK5X3hd>hcvFFM_vzWROw?XdG}*=e;Mylr!O4psnbvKz_N;>&w6f6QGC(UQxu*wLjJ*YszWFGx5)#>E@ zsQYi!>F`oDmrl@3;~Oq~{pG%l5TnOD$%+kt|VRi~GTqM}l7Po0ip50?3dsMD_YELPe#9lB%^ zkC7T*LZsBj5-sq$^sTy7ZIQIVW7ks@6;`%U8Vt?SQ);ZYP37#4xypW?E;Ez$Z&g_# z2nOp!IFq{wszs3zSGUO_uXSVh{MGXuRbvMX&MbY9r&2Yu`J1|QAN9_(2w&pqus2vU z(}e;X6*+INE0ATpBU2S|qENPmjmf47d@Dlw{B`QK`Arxn0d;++U@fzFZvGqpsb>i= zTwx8?jkIdM=4vE(C=Fc2;0EgwbOsgKTm#3)XSA=vOD?^j%Q~Y#t$oBLhdy>7;>^+o z_I94FNSC+#->BeV-ODUo2sZ>FVAD0O4t<>{pl4b6F3#?HGgNflNg-rU5gJOItf)BL z>n5gbg7%ZA3eg}(<)D{c=j)5KGM+<=}+vfBa zDOb+Z1C{u05vQ#ph9ggMcRWSe+q4hYU!qQR-31JD4i6&F&j78CI87~svt_Rm*)H$( z0$^F<3zwJoYFDl6g@`QA?b`dsBQVmTN=mv5d_S~tH;g$}GFW>uUN<)BW-~=L9e8nH z?3cDBj@;k1U+z-t{-<(lyEZ8P#z8VseL8p3A4Id&5Zg3#ix;$De{|srh)xvq7~($u zw!}}z5gj@MKufzCAa`40i3*w}+*Yc`HvQTkte9A=+K)dumT)*7GfOCL)GFe0ck>#> z5`JMkA6Q2#{tkZ}o(W99wpBCz@^<(W)NHxI-`z^RG8Nm4x)^+B1+8qQUYk0!tD4&& zm3dvhc_A!T#5Y6xRh3L}*JM`0D_sfGhucIGl|@ZV3-rBj_630;tlZ0WOfq9r_Y`@H ztQRbmGE#GzF^zZgB3!>h?06G>4A;NJWz*C$9?sTPJY4@z{k?*Djfja8qGJLFiCE5Z z?e4nxWHnhmARQV@<1M^djzGvB>xXp&OKl~FDr_&#o)+TneG1n3r6(o8&@@a&G#EY6#$TPp3Cl{4XKFF{&y`cMu| z@(*-}e1Q+wf};Bc*kLi}`XYsiuPhys^D(pBW8VJ(WKN+g3g}d4+*<;sAi04zrlmm*`N?e=L=_pn-s=sVo_}-rM}Nt3InRb z6`x@3k!mQd(PEaLbN!4(bZGDY>AWG_dGcfohh{x^D)41A(LLKU1WnJ@g(p@D+e7kDJ| zkGV@F3&&A8Ji8FAQY9x^GQZUQrkcLHAWnb*e5rC;# zGSW5=4nP0A5t&cz8OJu~*D>(xZ&p*m<=4?!ejN$F2G3ey-{1Qo^JM~-eUP5giC?vg z7zAh#2;NjLLy|1~FqTfA;MBhO;6J%-w=lg4bV*)aDV=gcZ{a%gW2`*;Dlp2hat~wWLH7S8`af7ZT_Xu=^K~$;8t3Ykczh^_bB~3xYO}%%UDU$X z9`jzCTW&Y6oXg>V5x*IS@0=?JTS%n~*bozgBQk{=cFSq;Wv|e%JXzT<7t+ICA!yQE zR@#6btn&mWEnik%>_pbWWMyz?PgZUfKi?uekQHD-xyA;&EqqKZ@O?i8&m=2MbH(1n zbx#*0D-qevWaw8s3Ns15C8_uZnq5D!Skm=Ag z!FvdJNO83i>&e2;v5Mi@OqvT)=NxZRJjj(|*xkg!)Oc1JeEBdT;Zz%>49PY*mk62E zl%$R~LdLL=2zr-uXsKq&m>fY5b}u;Rxq^bh{08#t&+kZnhw#g+=V7khNg#_Pc4kIO zY<*Wr#JOx_mGfm3XT$j_Qp4xA?10X3P#$V5k(?w$KPwO^R=N3dUG%BDl|xDDH7=in zo3~?O(`9;RAv0pBeJ)U$29;SM{Ml`!&p6t8yOMNh62vnHXFyhOol4T#@ zA`*KlCCI6DoWDYEhl?mE5ZS8ZcTvja2><=ddVj3mqiOX15uk7`lmC9D-s7iK6}g+j zQtt=Vw$1xv^H=c}Y@b7{@*S;K0|D;QitTZQ-GOEe!ZrQf)mMIfe~pPBL-4ofa7>yU zo!*k(z9zjgcM8 z%ASM7@!=p;_CKWzlZA&5GL`*I={#;-m3;y#8(9AXl^v0X85nJ&=zU)1KdWqoT^OM8 zLHXaSIqRr69ZHC8XuQH#*+s(Av~hiIxeL5lHe?OAfGOy+W?aJ}6UY=Uhm8 zc!gY*t+h68bJyfT`bu3hn&8{8tkCssJ_RCTQ7K1Cdm&A;x?g$K_nUP1TuJHyW~8>P z@Q`kBe?mx@s{Wf@6&9%ZWMBm@X0ED!M)xkjz$fOcgr4+@WEJAhm!fkH6j)DHKPNPk zfyq8FPgP%>1MFTG*aaZD3?4j)ut6dF_=P*T-3 zdV~G*p*L66ZxAV%a~$wc)hk$^fDrtS%Hi4Fz$*1rKa*e1^3|eAc_zNIWxFQUrj9eV z^%U^!X9$Ox?%Cd+BF5iZd|Tm*1qE&SeGiv!<+qvNd;Fw>^W=FX^8CzV;JEUPUb}82 zc~3rLiw;&|hiMyXqVRL*Ku3?0)=h>cSntuo9TfO*dZl5laQ$%6e4bb<%~#(d&Qwi3 z@b6T{JAdwAf8`8GY|9$88fl%c5wgHpd0-Y4jvvd!jD%57Wrzl_%)w%)=p|-KVsuW< zpRxi#@A;rgqD&1L~A4?G2sVBHLZnjf8vvws-a6+Ue5gDjlFhf^cKOn3fs z_|aPBn9{gdrCPI(TJ0*6;tkemA$xY3+qjh5WEJ0A>3fk7HuX={NvhgUsy_39l@4TX z=(-(x20kwp>(H&lBr8b3tdj+X%sK~l zBDUbpI(69RHe3*wCpGIVKH4`c?rKDTmyX{|#>@y;JO_CP-<+zx&2*#U-gH3Bnc=_e zIpeY^y%thDV^gj#QbJ34z~-}nMt9kFj!9W>CPlM=*IbkGPK7%Qn7tU1{6~{Awghj; zCIAunB=)^O?opE7JxMxUV<+9I!JjTrxBOXduARBeof_2IsB3EQ09XFhpz{tnj{3eD zb|kyPW!7NM)ZonAa^GL*mZN#`NU-)nlPx(GU+k0v?-j3{YibV56#6rMduGP{xL3$E zHP~Cv)U@9@r_fDap^-GFZx6br<|`l~`}^UDz`Uu!Xm4uJO(n_00h*z>N7X%hv#Z`D1M&l{-_7mK zD8c&yc-Wh>6;9w>Kq`l4cL%G~@nsV1Myw9NnG8r49ApB}L-?l8$f|yq6qytL1%0I5 z<1ZG3&_qK9_1_WbxU2;Lp1=D(P(ZnM&j~;GJiW^};qTN?W)GFQ*?6xKY)9jT%NfTD z|JERFknZj{zdOZTKN@=^c2C2}>Cg(wm><5c-go7!W8aUyr@h)TMS9Zw{dfyfSJ0rZ zpe&}Qb)Bv0LD!Ft;S0G`!H+%=9&6(3C3#vjFB2DYKGj)CXgr$_wXG5oGcBK$nHP}_ zkk-58wlM^+M)HgCJCWZp{Ep!FEBde-cJ%*5AFe8Q_2F`IZ~qVUVRE=S{Xf=+zqHHI zI=hHdTGxjI+vk*PRAh;At?0w|I^>jF=#|UYhqvc8I@2qZuMfY;g*3q{)T%x_2d5%m zAKvnouMbOq<}>oz(KB5<-?~1WCh*BXRNiBbK0LznCKW3A7QYLLJIpJR)rU8qi(ua% z*q%NtbAkPIjR(xrhac-6q6yfeF0c>0B3WR^3)ln!^Yr1T8rEdsDIb{Uf8Ub>>|7Vv zT`hopbq>I;6);a9-sb|l&`fQzou6tS($kB%<+q;ZDT%|J4s7$N+@DuRNL?vES z|8wIC%L=3qU!;ub!?}8&s}FBVO9guRa2{_#>RcLZq0xR_AJ(GkU85xQJS=J=&th z+X+2XI^UX~7%@L_m_>`<|NnUV5;&X6|NpUEYuAjXQ`Q<=NJ5N#GK0wsmncPLDUO&+Y0-lAlolyrV(d$Hr4*vV_e_?wi5B<&et(|lIp^GShv@tL|GZw;+;gAL z^Zu;Q=UL8ko+IPLzc5bRuEvQUHkZ8kl8t9zBroDsxH9HPkE>RT2S}HQsOl|!q8=yW zVS%BwbND6#HBQvmGEPKqg)-dbj9GcT#DC}?c>nyL$BB#BDMqY)$C*>`OeohQj`8sY zmG3ycNS?qa@YDjqMKKTEW=#k7bb0cGH`$-dy2VN?>pFNdHJwU%%^jk#UZxW?5mR3B z1UN(^?eHS?*F;#aCr7-#CVr^MaU|KQyc0sk#<^XeF*V+Rp<>yiIyF{358?kL0z)?9(rO#qunumHfAAO8xBgcW5e`5=Zfu;96XhGM^C_-69s_biD9 zKIb3$Cq6%>6YnD6AwSl?la#@wsGK#g7NoM1>q~xYA?{JRQJM|!m357b!+7y_HC|i< zgMn`{vsl#1q1ars{jw*&q;DFF+}D{Fco(Yb@nQohbMoUX(sUusBNBtY@!|@|>Ey>N zsDEoTpB88qX4Uq2P)K&XlBLLwmzmMxkUtn*Iy+v4QizlRcBhdY-$mkAHbHhgR`=Ah z;~}}f%yY0YKmNQu6;LN`nuok_gtfeYhif>`Vy*gNd`9AP3qIH5b2UCrestGkf0_*_ zl^D?=|C{yL=}pvn?B{s&{htSmC%A={!}Zv_%f&yZyB@pm7^~^^*kf1V0wJ1d@|wFI zd(j^ry3Lx-zaG2hFAvcyP1IfT8sb>zwY$9>AJRlBL%Qp+ySzx(Ya$sGI_t4VfryCT z%2AkaJ@)Fg%zRvrP5wn^zK@|Yvd>2+=mm4rB&wSU~1NN8#Ymor<6v3tg%(ou#=57fWFGP ztsHmT4Ba;;DPWdnXE1r08RG6szyNK29)I*K*uJY*>8_kXp2;b=%)q_^UD2cIY1zcA!iHKCQk=iQ76gm9)wzV7~o*#t`1w!aWpN6$7S|FxMXq z*fa$eyHMkhXVT`0W5`JtpTPdo%t^ut z`xj)p0`8JX-o>^B*|A`^hel57y|EPph~KO*=DSZC^EGg+%eD>pG_A30V z{JRXsf%a!H-$t#jViM%v9l5xUq;I_qxpf>=_2%EkQs(5}*`&FTG)3j#wJ?^Ge;)>w zhiIS~R@l7xw;Hcu4~|=tzyxipvL)yQmjJdi8(`ZBrt|Lv1=iJo z>HM4S0jsRQE=~Y@oM2l4=F7kGUMbAm0SzmCje(%azlW|C(Rc9=^^VWKnZ!E|JmlYI zH4k~BO<21>xQdt@?&M(lL5x$3OmU3hekK{P=TUB_&P5=Rth# z!Dl2sx8P%PFz36T@oz7j1eV5~V*YJnA$SW)zsOV?wZ5RDTyeOuBfq5g8-kQtr3TEu zG07SB&hNl7C+)T-O;yso4I2Njwl>aXdh5op0Y&V51F~9`E+|fIX?e`XqoY$OPEk z1k?HUIt6xt0n_>ROb^(l3M@GR>{fz}BACv%XDP6qJ++!p9{D1;g4bHjW1Kcct)-g8 zRWprX!vN;Xw-1vt5}Xe!z)A~P9Z($a-pi6`;C6!^pKmv{1Kw2NA>S_N1U(vzqH@-} z3n7*LR;sk!|M`C1>n{SQ`0R`Qy5)A^w%9jGNk{NGh|eB;w&Po%4$ClSvk&0Nx~DIyWCh4Gw3ya-gXUw5bN z8UKPp((q!I_VMi3owQfRzovW%%e`s(cc8?q>rDAlmV485qdlH9y$q#D(UU z^q5Ma@L;=Zq!4E(?hfG;#cVvyHU7@DO7ff?4x;AEzgCY-#|w<{ZsVmKEXsR~YomVG zNbTk5>T|KKiI*7j!N^r5YZrsRu#Xn*RIf4qG%GflGfQ(!{;r^$ijX|v3w*&*1UyWd zhqvHaL-m3&YpZyuZd*Lfw;KE(o&mz9i^m@;hWB~!hEx>9bPPE4as06NZ6$0 zp9wZui)z+H)J#GhtQ$N6C~OeKeY7?b4~C{TX`aG~KJxSm)~c6C_{prt(9|K#aL|PC z8av$EGaE=CZYXjnRKJOH=(RrR1^n8BUlB)XU5jXPq&!QcY(w-yks$c%0T#=1i8JA? zyt>#7_a=YCZ8biZh!<~8#ifi}kZZc`!FTn%N%bszcb=|a2b1xM&q&~9L6m0-djT;N z&ghL__<+|yY##){OR$IDkeNJ@r%mWhnaL9-%Ejmum`yfj9_J{?b6l4>wPk~CeTQmT zMMH0yH^AN7xW8x2dW&v>^cJEip9(iUlC>ydq%={*2r^CHfyQ05E~Th3VVkCq}E>}vH9O52bQaIRRxUj1=2fU0yE zE4>X?{?iVc9>M0#iBF;Dg1N84xK4W~f}odE58i@Nsh>J6E8}@w%Bcrme}^wTYG*UP zIDTnHV~bzP36}V!9kB4M=Kw)+Pdyk09|9R~NEGgC?c2Ym6o_y<>-kG@3|s@!z>-=i zUt_aWO8(grQa4ha#brqzr~&?-!?MoAWv(2S6~aRo*vpCyv5&QeGUaeEC|^RAvcDz5 z_ep?y569v$Q#bj3<1tfz%24+;mf%TC9s{mLxRoMgK!iw-G`>a{eacdt*B@D0yIR|l zd8O6iQOWmxS53~zy6=xNIIsV!P-J*YZuqh^hiT#`Bmxe2FV*h8OYVySXK=kS{Cp;w!T4v+C{v9G}#?V#VLywR?|o1z?MtMi{Dz-E}1Z%GI~3GvXzez_Ks zFkq8m*urKI*WWwpBl|blhY9REY(#@SOI=r@vcA5S*K={5ja|zvaNOSK-%#P;i?6dQ z!iwKWYyeNe831Z%kUa5C)~zaneZ;zV;+iKdHKU=_5?4xoH7Q@q@@r6T&frof!?tct zO6gvhJE}r2U12>d+|Hwie8O|T`^Ly`_8aI4CFvLz8(E`<)QI0xh#MKyrj*jA-K$!g zq0pwiEXHkuBpu+SH_h5bEW(zmdm+F5$pF|Q~_P!k!HFJR5ZVk zW-^kA(HM_$q9e>n_t-{J{z}Thp!7SDkD@QG<;ksoUJd)GN=H~pPAK!)M{8SYC-_8z zYc_hc?Rm)c-2w2+KTotNUJFo*a7}H;c;!oz@=7e9M)@Mv)ZG*xk0O^*+R|$`QYuAS zg{1RKx=JfIUjbr|o13{6sqKe8JaB8%qrn3th{_Z zS7A&GP-yK*d7>Va=e;>SsMQx>kjdei0k1){9UI{al6>y|nLhI2cO8#0*wCx>_ ziMs4}P4qpg=GPGuj3LxVxTcA2cSTIeKVQlpWBF5L6|s^SXKeN{48A2G6wTwLc^>4T zsU~Iw&GvYjCrC4zkn;crN)VL>No6xw7`b|qR7!>-rRe4U`#jp?}-a|$aVoD)o_ zE%Ns^vB;XU7hUdAY^Fg|QlrfHqm=u^$M+~Wd>gfP4AT^$=j6cO& zhs*uQmSUr;wHTj(8ue$Reu7PlI`)PG=m~E^Yz_#LCsG0TK&Zk=)W_!6#Ws zuYp87@8x1i_Geg|*YoPz^D$_Kg;!Jhjv#0RGut1y1l{s46oTC(sFj~07Zqya;%uC` zeKi&u@`Li*Z#5uNtRzklXt@0f4qEweQALdBaK;axELv0?GtuoK?jPeGK#W{IseK>i z#>E)ko5guEIBdf?TjNT+gj7!I;Cp~VDDTdf0XfI=@U7V6m-o)<49-Ka_2^D&kolom84wqB{bS%Eb*U=WgERXt$S z6}2x>SB z?suec>1L#PH15PtTfYY{sHd&p#y3d5q^#QAH|rQK)eX)&@U%6cJUf}(BLUKA546bk z@)#;25hKKD7eEvS@k$hZCQ?B?4VG3!$3FKU?P#Eo)Dk;tXrfu@AslueYogx5ZUF@ncFAZ% z4$)*yG(h!l?jzPm0g7mOq=h18bcCYn5$gw^Ve{aYzP4+1^LUXV9Htz|T?5RPz847| z4Gg*je>|(}-FU440~ZiKYIV^h?y~btWZ?x<35)I?u)*#Mtg-=PjG|=}^MLK-gdl1i z&rtkaus#HPl3;tqFToNDY@MbwZT3T6Ysns0V6zjzxDR+ilwi8e&ShLg1LF)Bq8n~C^WL0*qvkk!0f`ef%ko@dqj`S74I ziPG#Gv7mnJtNb|>GjUTN*4D@3DF0#j+=Nd*e6GYNgpXNUH|;&~{^sxjxsgKN;vA3T z`sE3{p)fhju#jydY8`ccRrs(TqY|A>jEJpg<2$dg13R^{%J4+rahmCZ>EV z%f0LAZvX}7UzqYQSdR5{=efOI>loLv@)}zBB}yUYoGy^ZH*7h{b9;YbwxC>#_4M1Z zvzu=pGWAbmzn{77sm2RedXBr8JOKiE(l6FQ*fZ)4ev*HG4n-0lP5#u$zbjS#%_^z# zZy9tOI{%*Jw2M2_RO;35-N@t{Zjp**-5*h?&)!0QMp`b7If9#()rP;0l8}quaH3Q5 zn02hCQ*(sTLSSENUgjklq=~?m3nR%BWHuo=^;*p49k5F@oyyJ#U7dImcaE6oS^1IRg?)ak7sF z)^c^wlbYx78Vq^kb5|3RSnq2RH=9<#{BFU;gw?5;=M@9&UIV66^B@n{WCb=b0c;5- z!3!=V*j`C2!NCfwi2+0SFq>BPfMqMNvlGBZ66}0}>C{|Rf$d>0Lp7Nj0Je_TTFs-J zzeKGyn#5JJF2T|Orc?7^ecae2QbvM{VFg%eKC1(Ysd*wxqJdEcJw7#miIsx|xxhne zUfYS3!ADU!YaV*ABzrbeBc8r1A&$B;4KENGfYrzpI9@W~&59dme_-^hh=2?@9+-HS z{oR0DbR+eiBH0bUU5nN3z=tf3Vxun(`^9@(MpWXl2bnpMTTr-`w>Kg?-S@WO082S| zZ?iBYw#n8OiAUr6t7j@c2o_(Muu22*_}Pe6tA~+rvBIpk0trTcr?%Vd21J%&Clt(}I=bt#+1D|j3*^19Pd=m55 zmT{I$D84$B}9)ypzA2>wrEW0Qsv^8W5dfWlk8Mzq(8^`Rm)oI)80uSgZU6>lMzD1`=#f%J%yHbp>im*Ew>`#aN#{fRH=(8;$5oUv z_P2Q)$4w?pG15$+xz*ztF>4NFbH;I{P^?B`7}9bR{Xzt8%z6kkGLkF9a_`QDE0wlW>dQ}x)#XTK& z3gflaOIc0lyY1C*QAEB=UFIR0tBH!pcW_98KbaRU}wHg6Vw6a~2`l{RT|uyCEL1bOkmj z0c?3?fb}Mr&Ud#cux18K=eueiFuSg(Svdi0G{G(hm@nV$V(N|rQvi+JGn3V!B*$x8 zC8-Bi@(=Zn&v$i+cNg%G@2Xxxrop>WIcpv=Ly}z#x2`Z=)A_Ek$#=cu^4*6p7I+4j zop7!XMP|NRUx{DRS7Hjk@EL0F$#*rR%*l5zljd2{#O1qXkj=?=FH&dk%=5JZ=DTM= zBl&JR%f0#T#zmfdHv^>*rWF{SM!vfP^9N-KZrMK6m3|@}2YiZ~XaLJnz#TlDU$SYVD>{m%xXvj+ap<2)n;&|8}pdX~JuKL;2HRW;75N|9y$S#^k6caeRK7b&EP2KX1*3jvA-Fafg{ z{~~*e4E4Ckeq^preKE%QW0b=@n3hxchFk_OCkNy|tcS(rKVIvU#phL-|1^o4viezQ z$bS;s+VqRd?|BM-y5W9M4AV@L%r1y%SnCBP%MWZv_=C6=_R{qppeAZ zoTc8QVTR79w0fY|0;Lco6KqZ+@vXtcLzw`H@2`EQmiXe1%KEYx6)5^>7|d z3O1c>t`HBuCL6mZNBYN=SOI zF>j3j8|T5i#Bb5ytA~%rA`@oLIun5ZIM#DcK5*`hAh|tIkxFgLfeUcY^5pkMKB7CZ zKW84R=zD(U=zAxzm7`dau-eb}5bdRWAX4{6o?_?;(awAi(PB-cvKktTCVJejS2=40DF*NI*+}g zz=j$y$V)YQdcYQ|K~awcu=fadAHeo^V~{!rgr7soNH7P`us`(h4-MfIEC%okDw0k=Qri?$2 zw%Bp_m&2KUvPd=P@u$G{QGCYZb0HyHneafN+IfJh}_diwzDu%QGPc6?a;fx>M^J8Y;0*O2m=6`#C`MUjTzI>Ep3zb7p_x|!FXNWUS=l=4XIjo{5#VhG@ zgs>VMy7!lldx>7sME?8BQ|Ee+?$$&qhhmXOr^k>Nsh1{FIn=$s{GJ!-VofwaMS^=1 z<41rZq|0%_2L;${&Hd$3V-bFGe|g6&Ju&W)6lk3r{2;8`QyWfh%2AEe8R z$e)Y6MAvB|fBw9Bwg;)LCi3UcGrUOWXd-|9yxoiRb7>{hfD`1;Dx5I-^5@DsP5yl0 z4V^z%B773_=ida6239f?dGhDGyw>@10j66jD13=oHegA^XIH>(nK%z=g+uts3(8YOKJ9{-XuEqm!gyC3Y%}Ar~da*a8h66I*9Z?$fuikt9<$b*L(h5K7Cgf zDo$&^W38EVq0Xnx>GdM=>1|%3ikiruPs`5oARQ>Kko@`dlQ%s?t2B{6pPuJMdRY?< z@aNN`h*U&%6;51z`Sk2zCZC4T`6K$uE$CMt!5c=Exc!dr5FGnIn3+8Jl#)y;^x!&Nk|OW5LV~Y0Sc_X0n_=E>yHNPRs~ie0c3qud zM+26oz?NtdSF-a6){S5~pRQzF;QUtu#x{=eDAyki*k_m(Ld}s0U|aBH3aRWqIL``gG}E6? z|Mh;ys`8*#;t&<Zzj!Sq*>}su@RWdNwH7T4czbeBAQW1FP>fp3Q4d} zv9yop{D8||V}i{xDY>99fHghYt!LMID5|HGq<*rq%!r z{x>woNHbZU)>qF$Js~kDUNkg+kmfefoW^+pXM$f5=LIy7_>?G4$xjz4Q!W7dK9L}} zTvE9@F96<>^8&`a1Ie*HQlA$v1efN#fai0`8<+AibOyK!tf;z?oFJdU-l#tv&d8k<~pfAdc#H zJjAch3mB)`m;d=_=ez)A&KkrXht4FS+2NVi12#g*oEK0R$xHj7HY>deR{n>%)Oi7u z2^R^TkCvj(3rNLaOwJ4Vj(t7!)8_?zhD$jw;H{VSc>yosi{qD)G&Yi0B=9v_jea-u zDmplv@vI=Z=LKZKhd@rASW>vJwc9$Y_DqqX9BfReR0Wj(=R0W*FF`M*3YpxQLl)eX5y z5r+l*HJzfI!Pvpsfh@2N*+bGI4q&(4K0Z)GKNvs<% zW!56BWu=oG7BC(`4&QYg=FzsUtP3jN$zcJRqRp3tGKU3p@yeGaz_ z?N}{E4}5gfcB!-aYC8H07prdG0 zhfwCQfE&H?WlMP*mg~a;ju4h!{tbI`wU@b%CSQ?ekW?QQa4qAEq@TK^bzRa+NSb(9 zfF;MY$Y_lbbOdXW8oM1UoaC^8@w=UXP{L%f!buJbxbt~2QB|xq%ZVp!_UFFsnn(@{ zxKvEgl~CrefcCD4DfykGd?3s9VF8~Jmd$<(w(LqkD4H8dgEACaKZ+Sa^KLxNAkvH` zWZYo^NwA=k8Wh@1r0hyG|5OKKt}(kjK{LmK300*btkg^@#piu}Sis=t#3EyddMt9r zTdqYmqiTK*#aom|s8{fnhXpkD%9ktU5tfg~S7_VA1kGZcz@meuI;B#iQIet;BtyxDd+;`M8S)f=}_AJ}ltfRL+ffSit8P59`AMp1?f*|Lz%-c`z7M&TQGXGad;kB!gatjG>d1hON4<+KXC zhsevrZN$K(%)Xclq+;M2iln!|Whk82yYNW~r{JomF(tScivo-AMGiE-6i;aW1QYQ^ zOX2L^fLJDZN{$ih4cJgP{U`jw$FZB_#!4}~ujUF(I6G4}!K;AR@62LJ1wF*dXIw}$ zyKp>M#;m^?jS#35WbVBm0lC5cBP!K#Ho{?`=DmaJ=egC(O7;EwJLyC5&tYI$2UbUtCr&AamOLH*$vC!6;LU?Le|P{+9J}lQ zzVzag6RWbjRDJ{uD8%|*GbI+e+*lM}hJdrq6p$x9RI z5=++m6Fs1Z_;B-rXNq+5U5qwK1tW1fTep;)R^8I>1yB5VDIWi-wmK)8RV*ibX-Z*= z*oa4t#jL$BC!fFi>v$}dwWuv^!+^|M)EZy+-xafNfaVNLET37!gvIKd$eDI@8S9P2 zF@w(o!SC)9gbNuSIN51=6MoysCcs#^b$4#$x0to@hd9WG33<7+>T3x3{$G%%P5M_@!eDsirDH{+WFao1 z0Y00Ab<`u$^I;veD}IJhmlC0;AlyWR+lUZl>evv9c0I}k=rlX`JG2@){3U0k2 ztk)d%BH5X!vkgIdG!)J5E^F5~C@Y%13Bqg>C_LF{cX=OcdXZo0{4cOXfu=D2D4I_8 zw&V#TF#H4~o9E-8H6s<^7%ilNPD}-`5ZcnDxj_ zdAH<50u!E6nU`y1IHNHSmxZ`BjPJohj}Xn-fFBDuHq$l_c&KOz+|9ho?}c?IX)b8~$_u`7!HO zco-3rJh2yS13TD5A69&kD)>_I4)XJ7jIWpR#B}5^jF&v2I)BxJ0Q1cI#3NY|oY1-> z`6(oZzMGiuFq#Es@cw71s4O84!dEfR-yBMzVQKRm!lg0Kr!pMMxCIlD6PqWGa;pIW z&9ew02=g>T=N;mstT4}ZtkjEnYAPUQ9*cyL;34FeoJbGk3+pp}4F`s5fy#@&V85d@ zPd8eV=0W;Jg^hOChxix8$~;)6!ljsJJhs$p^WZjR5$1tyU?JLuCW_o+L;Z8)9U5xt za}E4SR&!9hh}hfDnaYdCy5xRd)PmyrdK|A)b0U2==_D{5Kd7dj z-k=;^Qk2;E6%)cW$rCb%;@ot4csijAJZBmpc6^?b>Bcx8z1vXT=_GEI}fp)bC+9l<57YdY9=}I1da? z&WUtM;fbZ-9j^9iY)Lpci&T+5xNiuB`_q2zRZi?> zE|j>=!8eX?asCKSaW+QD1cw3RE^XvMKfj`YWs0)M0`kk%_(e?M{L%@( zi0aNS=z_Y4%FZvf@QbKgNwUdGB_8%PLTyN#6WiT%vF|sy&H1O|3F-8gZ|S+OMAR@& z35W!~Rv zZ4A=%laN+cq%A?(5~Sr$L<&3Upmwao=Og0VTbi`DYo748w>Hw=HayAp))u8jwKq@u z`sllV*S?}y+=RpbRr}I$Kkac9_h|`np91M~bli_2)hWdNDaSkGRGR+j#i`%^^~C<| zqkj9h6Z_xx80}vhrA67F`Of2mCK4As;qD)DoF^Pq`!-D*^F8t7zw?J`?97q=e5XCt z=TYUUdI_F-kQ!=FH7C_6cuHw;%Kl=e5r4&H8p%(m<3T6CJc*6G@%iO`{&Ir+a-&c? z`Q@sUkhWH&CciW|5owXlTet6~TXc6?31clpK04B-i%nOmnsy0<7J@1ql=h>#(wtQt zQqWyucHjxCud%XpN?oToMEv2#(>uHWseS9%sH9sl-Ywwky{_@Z#?LZzKK0%U{g-rA zOPTabp{tVa!`ojV8!|Po*iJcnq~wIN*j3EsC0)5?B>MXcFbTu{m@+w$PchLDyVo-q z{`RcKFU246w`VnexdifugL}ea`kd1kzg#InaSCnQXa9zn<^S9KdgAu1I&rpi|M*3F zvp6*@axBu8VW(UAj)(F6A6oh;w{MOu;@dY{eEj%>Ia|gbr_;!*z zlKg!fQ5_B*Jq3Sz8r%?x#Z&M#CG+_^LnV)Y)t{c&-`cBe^JbA8)?eH?hckH4rA2e6%YH zsK2&F6_NFH@>QZeJ}BBAaBP zQ_F5FETHO33<$1g9jqXP^&fBv7v4+4j}_s4hHyCufs%ue$IKY(A7_hHJ!=oc&eXwu zz>jwv24qLGigUdNc)ae7?^|hLeDBWxa=7@A$#0k|gs4W(+Vz@l0fro}8|{pj5^nU#!pj3a9;<3yi~JAp?TC}We;mC@cR zc05Ks$@;Y*%@ddnqs+(ZJd7WF@op|Y2NvA}5hEbWtE1BU zMP?OZ_E4C60U)pL0A^Wy2WCxHidb*or7&9pbCL&tt^S>(i2nLJ@b@U0^9=q^XNXJl zV7{z*rD>f=a5oG~+Zm&!8l$0XmUvtlOyV8JE7GZ1*fkaJSH#ojVz4(K(#nlg%x8S2 zw|q&3P~_84WGTNw+;k=GG`fc7XfHJa-$0@H<&}wfZW7FK7yiH;2ehiaY3J_HeFRO9 z?>gurf#*nwA84?Zx{wa}W32j}2D{WX*c>HuN#+qsuo!H?t|N3%@%1TP3_NKA&5WtC zqCId%U+^CBtTNYT9cx9J3kyxasE4Lgp6!61z`0j23>s=ZW3P_j4IKwVgZagT!cx+W zG9g$~QM~>j-ANM2kOjrO?i^}lMA=Tb+KK;@$yby+?JXkSx5hcIm6MonMdx5ioMyU_ z5gDC_Dz!DpNi)hSKP!I@}{>bB~yh=m7G&b7Re0+zAO zV}<}*wH>9Fj`k>p%q{k2jw}uP_Z3hTAG39=6see~g`g@C?R?zTYS3o!KxvwU!C6Z& zpS020T>h(~c#&NbDT*n*_d+Oc6QbTt7{HOh0_qg8ei3m+wRx)U5`(^?jQA@T{#O3l zB{tIjn&eTfBvgAql95vF3+=xXtL9Dr#bZ`)#&iW>Ghhk8oq8Z^?8rXK+mL5@WXIu| z!6DLkl*t$BAUQSJF)kRnK}_Dzux_GczjY*JhwB;7PvARDem#Xo|EOipgk#=A2~}X~ ze^P&E>QAPc_?=C=gZ_@DJx}pkV`6Isj0416Y+PC<%cP6#L99#RY8fp%b2UG5_>wLM2UT?w=rQY5$;w4Rc5MfQmDkf@U+B6ruu%Z zh`o-DkYdl^&EQC&Eqq`a)8k6)>&PZzJ*U0HU^`cThvmB{^$(a_(pzw>9cZLVh1TLF zZF$TZ92MB6ERPC9bl{z`<eM*x-*S_e^aoCbw6CDOuGv;9kGKRu^Al;B;u9W6;4a+5@=6oA`p8YF(@`NBv=Rb z-R_EAMTvb2G=Wfeizt$1TPd+0F)ClG#O8QYi9O6Kb|atIB|KvH($R5h)`r*>l-Q+B zP{Z#O`*w=0dx&3dleRcgwZ(TlL4AyxsE%Tk!bvtkJsvnnU8Q*)v37$ngmx$+=k6kZ zPj<>yCqe7US(){`L`_dlGJ2XpJicLA*0XkFtk0l+J($Cci72!aLXhMM_kj*KcHhBO@_8Nc%iIo?%kdsu`H@ZT zyQ@ml$TBgj5*)N2$@B_ryuh8j*r#1_E51C0f!|l5$in;u2H1~BG;ol2?9-Ta5rv9$ zIhqs3+1wqD=0wVH0XQA^f$3}Pi5OWwXZ$iOUfy*yDeqANouPstgH~|V7|%N?N(uQI zzup$j_^}1;i+!(WLkxu7!&jiSG~J%ls`_s&Q4+3R3V-sd!=qR7x=mmj+zYxv%shvh!y;7kC*!EggKb9$2E@pq-zr>rLm4f4>Y4y{571hNnwpm*I11;)~yQb{^o);8O4J2 zE$P|>77znEexR_{^4D<2a_0MJM&BFohZftjLg`pTVcmEmsj81cv0$wrU7Nsd8f%ck zdXK+`Gj=PvuDf1iJ*%-Whoz1~1`5`lC>E?Wq-ztXsj;dktU3G@aYf@Cd z6h(sb6lw6DRgLqY!WqY3!x^g)gytqU;*NLeu?XeFVSjV)fh`JKU~@B2D^}+MaKrla zm07&|f=;h6X00M@Bqt?i1*mkydc7fY8a8G85zA}Hb^zfg)?60k`e!Sc-^W>`-C1KqiXsmDV(p zM}@8DE5UoqVMTb)`uZy9o}a#85>c5L zzK}t6lwhAMVWU<*!=_aNn-+%6v?V8IgZYPIGnGVS^Q>lr9Y|pFGr32tWW#2QNJATq zY^b!cx%j|MiY-paX3VYo@w8R2xnYqmEJpUiyjj7o6ATz@gbRNYB5>VeY-Pn;*=V(A z4WEtpu_N5%+cK6tF*ddBa7SOd8yhPxJc1B~1g!>cpUjfl$;1AJRsniCaRp97-k z{sJU~W{a>NPGnuSe}(E8^#AJoM((3#7bGgZOf8m+$#-*|gbq1paZ-|L zUS85r_`}+d1J$1U4y7KOQZTzIH=!28Z-tTmtR0wBz&|-qwy;c2I2b~A&Hm(W{u~Zu zqbG+fc@+viLd}lRVZ{osK)t40&~6uiJ>E}2GlTvgvX)z;yLYQCY`-VY^_Kvju{;1o&Tf$GwO1Mkeq7WLe zClo$Q4Z>6+a69o@q_SENIA(pM)a=Hgh!Af##6uOkW-N&Yw{rC+h6;OBg{K^KcbW=q z1f9h5Kpe9cD9Z00=v{`gA}BF#?jh;cdXCVM;FBC6kwa(bBEI?-^x9Xu`of9#{~n7; z!S`;#N))t8zv(6%Ale9!U*HSUp}fP&9JPkGz2z4Z~Alt~AnS_Xs?s&5LJH4J3( zYWLtEM0d;@0KLT`$LKGx zLk0xs6M{G6o1HH5*-E}jrhXaJ?^?fwcRMt^_Yx{#2e#ehQ4i0mrq1WrQg!QY5GkD| zV>pBRAP`V=sxCSeXA7SRQXhQR`gzbJMvi8iH@9Y60nty(S5NUOQ8-w51-2*8#wB;Z2HL zslw&GMa7XA==xnaKnA~1G4L*(>9oxm;@T|q{egGl3Z|=q6ch}NE9j>R82D9bHwP-n zt8i8t3N9cwr=Xhf&cZl0uq3Wvjw;~VT25R+YgNz^1=Bemcc7k-(Ro%T3cA6OZb2rm z!dV!k2CB!2o2R=ZJcQ?{BKtlCH?E#_UC%xXBhY;r~?)62a&SP|=Zjp9|daC;9?Fcr}*3V!K!1TloDQZRsT_UdsSS-&^+pGW<~_*kx^d-?qyp8HIl zn}nw|9Yo53b&$eh)_F!hTl6bS{n{qN7vVs;PY|=7{=g&QFN#~a!eiYuHB3&*6_!`{Pdc%)22C|)Et{=l zr=*L&`?5s&6JrO_{~*T@JnO1euqu*t-ud|LOt=j54mqDG6v-%o%l!>tulBuRuY(Xm zR$iLn3eQpQ<*H)T`UXkNz7DMyVTcEhF{es~G}+P7XB75?ETJ|Te?iR1uE@s7s^lrl za9#3!tbI>ef^V(g|PUV`a%Tp^ftqZGlk8FlQldQfDne;z?F^%7l1S%syAlQoXP$E1y5Sq7zG z)p7+_Q99_Om&36MNEQ zHyNU#B$}XzT02CGg{ZwD$|F&KMPxZdS6oS?>W1iE617%Dhi4hF`avw5my1fG%0ncw zK!j%ZCyf%Rz77_}v$R{8*COd(02GH^ltx^{tj)mYw$YZb-NZU5U|8U;OauXf6V-Wc z1WMB|+p)X#*H*=%?>k-i9qZABV2gG?SyJYMkHgu>m!a??EU2DOhr zTU$=#7w?Jy)Vqz{gbL+UNGGAZfWkR>vEkdelu*9l3WV!cXeOat4Oa-|D2b!i7ih0` zZa3P>#>4Y@6$@rTpzJ6QLpW5}isH0s_$`D*QbYr9)9~P1k`|jT#`hZX-ARmC%f20y zk{DA9-WA1a(^)|JF*h;cGdaGIOc!Fk=E}E420F50!F*-FI;{B-YC_>#5m5da|J4jK zS+Z$_@U&kZ4YRkZGajafEAq~zEhGsD)d4&GV1JMr*hFEGiR5%w9Q0o$kuF~em$x<)~BRvE)`{KKpl)1 zmbg0m#JIzUnO7(VYbB+85lw=Gm-HD z1Ku*sUd0udy`~~#C6ioO8mbJ%jP9cv6jY|}qZPY$F1uP;6@Qqv10Ud9ZaAw_Zg@zV@^&V?%`>NXK9TbFX2;w7X0N)A7!ZMXhw2#Q zM}X{ebWTLwhHZKzRKE$2W|32xz_cObPdT7HY$z7Vl5WvVtb!NgQY^A01g)n)Ke5Oh zT)_cnkvM7v=zucJ2a(kweb2Y+#5EABKE%q74sTHSOO_~{4@%834Q3RMF{W_cWZ?3{ zxD<{p*gk6-j`dah+(P20^*xjQ)6xv}=(~X!U{r&`u;SFiH@TRFr4W22Ckr6V-;L$II6f1oV0F8hB&v2x-~Sqog*BJZlOM(r4E1@5EV0k62gP8 z;=4x1cN;Q`@SVE9PT!%i>hrrWVbG@p*L+z4QYm_S89D2xI2o?ILw z6AF(hgV_?l=7a~QG(D)k5Z}695UprFt1DXz9)5Hz7@ULPk6G`44MvA41($^&z0nfdiDEE-ZWy01SEYq9^5X zbBYcS(;z&haMTSsT*5GZQUks+kUwS>pRQeXgShHKy6QZzNeqX;m$0$+)pA;n%T?Q9 z3%8LpT!%|(B-?Xgl}li8@zr`AYZgn}e zM;+l*KQ1bQRZLV|mkqSfA+d0}0$1Sl7y3bP3ZSx`BbaC>y`cdC>WNlZl$U8f>`vw535L5ceer&n4R6?-bU zWyay{=yeyuRP;xCm>(w-Iqe`=56LNJ)r5eOiY9vgz^>J4oIT9+MI zqk?CoP9t15-O@4gM-DPNb9Kka5BxddQq3}Q}TE=>T;fLLA*k^&zi&?J% z65LA{Oa;>Z4~^}7%(3=WwIjEk0paT`24e6QHWj@EEoSvqFewGEh(L|OxbS9dc1Fbo z;D~D1%Aa5};d+rFpC#l?NZy8Pb&No$0UX#uj()ie2gl&5uANtt^ z{;T2;^%iH;G(_K#sIwya?HO8xytZFLeg8%!=v%H}KZ)unqD>CbD2aq^il}bEuONc? zm8x)pY=NZtP5)B(4JaV+`4CgF`AgV5tq@8V{D$)VpP>7gfTXF@ZVjegb}9FvDR{b+ z;kcA8<$buu-Vd`%qJJ4zNVivzIBM--chkQ$x$2d^;}kNQ*On{i*FqrxpW`yyfv>`B z1lyT2$P3NP zbl3XLYV(d*$J#jhbRBmc$FQtma&r8Fy!8SEQtHkSH}U53n@Gvn&k6UTX}K!ba#iM* z+C-HDw@WUU<(DZ4Z;8`o>=vB4WWx_}DRFvB8^q!lP(~U>UtA$hpC)nC>V?X7XRu?~ zaSt;m^(uDqZb9rdL=j`JF~o>j#dMr01!M-N<7_BsTWH#nphb+WW3y8e?D~*a2GN`7 zB6kqI1eY8{)0{F~Dg$rX4UDLNz+w>fc~(OErKr=(EX|;+?4(47RaS?)?mnM+#R#Ne$t1X z;bQ-Y9u^MYC`PGHzavyh8G2)#a1ot)i=71`A?idDjduw$wAPAm^gQy-Wo4h(4gx71r zvnX*S@cHAkI!8sj{6=8{Mj}OVz+2 zr^<5S*3VyMkXvO0s^EMPQ1DhXJT7xa?O|$ilh&lO!v13tHR0{PXJI_d|NPj)UF>sI zl~qoaTU)crMu-=e((Z7p?72wnHN~k?Tr_yWUuA?_Wj3lHrHrK$RTPv$mX|2l8W#n@ zl_m;agC*_VkWQlD8C)R>-c(Vr2F=gziPrAxoplNs0h8RVP0~VfjXpid za*txI{RBr~s`XVYmjOA1f(Fle`Pbajw9lENAndDyA%9z~>A2XboOn zQSXcx>JhMi%-V|DBFcg|YQCNb*zY;i!E{o;VW{)tsF#2`W=%$+ZU7YvJ^=yDC4%w| zRY(UnvjM6`*&o9hNe8zvGMvG>)gPDAC@Vk}n`V}Dkc2BV$}AE`t(vH8XQQF}I)#b_ zE5S57ddC=Ar=eo{8ym7>I+;wlHdvO;$sN}GfqWJ>tZ4$v*gXyNTDSu9&p`#|UCDek z+fMx2AVZ16nu-OhMTp@d#GAMSnH%9kpgpAv>qsiESn#Q`j?hd1O;M{jW@UkaIs@pw zArAaq_F}oy%dw3PW;{m%kBL7wfJN4#(c1gxfr)>p5C0Z~DIaHzeSsB<9Ew@XAJG7t z;sCy>NAUxXITe~ousv<4H^)))ZQh_B;81S>JkAv|)LY`H9{@Gn(wS~iuIdG2OMJW= z7at9$oA`Ja;b_l>QW76Ca0M6Lui~Q$D%*7~6mzRRBAmG+O^sz-Sc=(Bx>GSLhh9^d z)n{UIf-zVHmlBL+$*iMc_NNNQztETw59?c?Y#w4o_f2m)aj;XE4i}~~>WYSgU8d=% zCrs~-V`|-Mn9f4lus1?j(Qq1*NHlO6iKEt0251Li8h1~kV!ahn^J^rfiFjOqq=c&5pd=Ti>(A>xtH)1i< zVN?ZrL2T7=b;UQVDyG?w<|t5Cm_r`K(LhWHbiEF)W88b|LQkl>7bWP=9@sPyG_V>` zC~~Sfu99pHe++j{!_1k>p7wsuDdVj5hX?74I*AR*X}ys{Ug0>Vm%d0E*a2x^xUr&I zbIiK-A>Gi91Kw?^d0^n{TVTiy2*I3auw4nZ(mVUv;lWnUv@U67F3O6 zyR(*O`$r^*G8opiCG8>G53TINX;zflxR%-}><)ro|ox=e#(aTYv-V zREVpnXW|?7KO5?_IO+_MFR+(pi3ZO7z9PEYzmlM+94~-s1UXC@EgA6NmRS@Dhv@=mk+^KW;yu!7i7f zv2<0FXtN)eL%+wb@W|Ona9`9>aS3p-4ou5>+HPt=E1{aEsVaer?T1Z7=6?Gwph-84 z*Rr;WqyC`=bvK?1e0RTbTN>)etmjOvwsEySlv;r^=(cFE`tQQ=s`|bb+n1RU#;=D^ zzPf?>o@T$-hAd=|kXn!t2isR*W%553R0)m$fB)Kg>7tAsF|m2R*OUE(y|;Mus8zBd2|*{h+uG~M692Tk`VnhmHaWt z960N>CMS#bjH9lggxhzY3{V;6GmEWQTmvBfNez8Z!JtM7!b!}U?LASPgUXVo2O1d0Lm+Y@VYS!XMck!Boc3k z!%P;;xLqm;%uQnHVBt^HH&DE;ug%V+|8<*v4TUOs{)z6}(SY;$f~gA$EXb$`NmGT4@7Jo3WzvJ zH&QCiN>aUPDOQREO5H;dxEj_`G9sVD5Ei52sJB%kS)jKxgkaVIsavj|&rbK#jM`53 zoNw&(8P?|7quA+s|k3cV%mc`dLy-9MjYHPm5cHYpK6f7 zt~s^=?mk+R?p}s)lw1?OOMCm(IJ{Q`&$sLz=)+E5O+c3z&voZ-t|_x{DW)8Nu(Fpy zOErFFkVONvNF22$pt9YfdJ(34Jx+@1kOCgui{`D_K7!a04-R1vIcytVgG*uiRSiIg z4BNH10^7DE#ySZ8*o)y^y)v3w@MavlcPk0IQ7*fxhyY>Nnu7(+4)NIlm%{FvI)K~> zF-6M|uD~uxVmuE=3-kaxCqCbbW7kgDZRKW^2CbEcpG8Xn8-((#IMTB~ny?gtF%Mkm zU3k-B_uFXAlR)ps0j)iUMS)kw@(;pJPby9LN$fqy!f@9#PKObK_G7Jh?M}s03+BaP zjs~VL?J-f9^&PK(W@M_8VYIe{B4QNx{lHYR&L?r$RRudC4q<1EQ?vKdm4QQu=(w5X zM@SPUm(Na9-_z{USQ3v{V^JKd_s?ci#76Jc_PWMJ4EW8M`ZyZVU8)fwXXb)e%<3>k zOTRP@vM(TgBMm$U3)*-&)=8x`I!o3=CAjD?dkWRo8X}M+3J>r%C+IVF;!>jUSBPWx zf~h15_ffcLpn$|t>o6m9ZDm?Y&k;Aov72uRyZc>sSD}Wm+scOE2K`E03cIJs&W4!6 z?jgnQO%g|~%4F9E?3|$A6vwVJ*y%9smZuECASE?u%Iyn<4gv*5>L^2O~>ok#YcdN`QV7$Kp<~@OO*w z-owEqx#R8w25|e{BVo|7VXjaHK4%#$VFO}xkwqJN_85=v0z38T#5zr zszO{FvWO}-;R>p-ok5kcsBHH++tb{Bh!d`}2p2fhWfy}Dgx%|CNG`kJGq@CXE2{u< ztYJ3~S70}m#8GQ0D%%I)%mjxWh-3R%MTwK=YoV4(Du?k^*giwS9WAYgaVczfp#|8* z4O_Mcu)SGnIR};PQNq?YojerBwh!3q);?f3h1?BJc_{^l9jd}sO0q%}=SDs^%aX=_ zs=cpcAIY zY_*hsxsXo+(zgTz4p=H=@7AddPrk%61H}OpvqlZ`%a(*5G;0wPd|_wGCOf1yVeugp z9*~AU+-~4yTgIiXCYr>rZMY`#t=w7}@KIO14ppTsY8A6)!gNSmDFtV^s4J7HjBj5N zvdGSJ8B@VSYKrt@WnZ`2kBwDNk*-0Zno7Qpdkl_WE4WOq0~1+4KeAl*BRXbC5c`s|opcH1ggG{5`N2EmQ4z7=cEE`@aVHT$gl# zB3lE71}ji;PL>LX0qC@3%Vk_Xoe~K(20G{Tp5 z&JQLyCuZFRLNi9uU2{r7BdPQQ4LkvrAan?*I-N{~a*|Fmsg#pW>NLTn%Kvb;JqO~e z{EsW7lT;E%tv;x1*F_?5*Rywbowx^yqKV5hMR;2C;2(sIEM~8}P3zOtB`ZARFwn4y@?Zmm6)K)}w$FoI*kW{(=!8PmBwoaw8z(HAyO`Ys z6YJ9J?3+oF&?G*zOq2K-O_}+ML4bX~w`x6GyI{qN(lcf)0ja~gqmY*>29IqiFZw)3 zT-5>m2fB;LuA&zmj|Ds7QaXS^P{=L@tBc2a;|d~%dx@8V+$)UqzPomkW$duCHWX6W+oJWFYHm(zi2-MThY8MLK!+mxVpe}6-IXrV z-9Yl|#%CD5zuCjNztERs6ox>LyfpGGYRq$ahiHwjb7|@+n%yw3&|GF{u6JolDw?5& zrh%au=+f*iBfJ_rnoNTY{~vqb9v@Y8?Y}3D0sWFms(^?2fLyu&OW zj2HhUibL7m$a;|-b4S3F+@kMOTD(QKBFZi52E52AV8<=`IYAHGgOoY!3ZrmjJ%m5j zqENgp57*_I1T;ty&Y6L#l_96r>zz+*jb|Tcw(LC19%yqP3);0{1W{%m*;2%$kJhQ) z-4+M&QaZIx%Cc34L7&wmE9AU&*@z`$avU(hY>$K4%V6xSlrfw=W=}%mcSmn-mYg`SlevAmu8ep%8_d=pWkL>x| z;@K7>+raL2`G+fM$8tMdV{iEtN&YQfG8H8U=xq?<%fxbQ+T>GrkVyQ{EdI@S@dK$$ ziw>68?DKy|=-GFm+5}$SOzKrxH}yne=H(~YZ>t*f?{S#T43n*!TPRB+1A=uYn$5}{ z#ejdo9T;_`n+Se44r~g54QL<6l^P2n@8Rs_HZbppn0cWEKgJ*<$JA}0L)7(SWM^G} zZ#?T$$O=CH28pp`gRg)imJSZG# zNT=!(X2Q-VeiW~B4eC^r!z(vX^~wnONj%G3iB={s*2ytza<^LB{Gb}?aK-}jhd@R- zE&d5TVPkFNohkG-?xA%)@yj^mlORx1XAsYs%5HG?EFGhJ7vOpJ03O_c`k9iC!~x$l z5HQf|2YH|$+dkzUxoTJvPmNX93F$`{UP_+YL@PrH4fs{O9%v-=DqI7HBL&DefGy&V zJDT%Ezyos5^HU6((0d>_WBx=O;*+pelSqQ^AyUs+aL9b(5K8oZ6L|r4C~%cWD%UaC zD-Z(R&L&;}FCX-@25X0GN(L$WHZ@@&zJ%0*NZUMoy_l%+j{9+VW>CxdckBj9c1MHG}?D{SM>GZ{esXltJUx#vDLw@AI`q>-n zM-UP|&yeuFV#%rF=t24?vi*wejUFTlQpM}6T*vrDjSP?AFDKpL@aF&Ia zI}sk%jSqCABX=k8z!y-w;d%Q!;ng^#6GAn#$so;d5C|8dyIo#17AlX zf{xXV=e!Ft9UU(+gz^l%y(>by?}O&X83>>Q=Z-3=XqRI*9I2oMY6ss!be5yS{iyJ%(1V`B z)4|L4>P9-gUc?}OI1nV=Uxy3O800Dw?~)eJ6b{MDySq2F5F1x@*M1g(&X>g9q3QItGMs zk;H!@Qf6F%OG(bt`5r%9dqXRI#C`Z=$$ZIz*oRH>0Y)Av8)toq3vrV2_x|B8(Wv5B zIt2lI8giY46mC4(o~yanO4QDJFxnA=zKng^r~noQivhUyH(sM3=Xh8c=z|i$tDtBF z^f&5H{SCCw4h<|;LR1*8hqvG>SGbma1-@^UwNH4i^je~WgK;*%18LPOorr5f$V zQD14nN3rl`RlYmA0?F8#!mVvUYtiH)9bazIson1D@L{1O{7s3g)QvV=Fxc-kzLev| zxqIVEK9t@2xGoHasV7%z~Y~&$uRWFn|y5KavQ-m+h;MT@sCuQ~& zc7?|4;nfg)bi=ceTadNyTC4o6$!EHjPN#BkNiTQm&=q~@9y*hVO}F^Ona6mMz5=Y{ zPzqe>pa52P@wLhlMDqDVXV^)C-XlH-{zOT9-xzc?GIpIk477IG!&g4jm)?CgB|O1{ z^B{N?)wKodwHq^|u2qN~EXqZ(L*6jt!ed+ww%jz`V+8U&#+xi6@h>2hZjoTl$;0VssT%R+(=Wudb));Rpp=92Kz zl*rYef;WCa6e+ZSgzO<*H?D9RmphH__MY%!qE+Z({@fJRGF`{*E~mS-5WX-N$WdRa zB^BoGfYdz`90Fd!P_+@iYBozrV2h%z$JSA6=#)~h*b`0X6U)ueDaZ9_DPCYPv`)jx zq{w2hpZsU786HIOyjmsU=qx1LodNnQL;@q?+)KpCfQe0;jd zsp&>@Mlh}5)jl-kN?cC+Hdr&>|A?Ufr%?aH?TP+J1J0$tFj^mtf*-QK$J~9=!V23I zD=Y#lbW#Oig;r+k8xWJM@G7ywDexDl!>mw5&tX?DY&B+u!)xh*wCTWso!W=4ugG<> zQly?Il~`zhm4Fy8hsLY#X`_?oM;bq&djfDSjP%oW_z0Oj4QPa#RF|Lb&IpdCdkc-D zk^R&Y@vIhZa4Q}#iFb9*>FyNbD&Tii))vIIz~BQ*`Gm+L7^Gnk<7jZy^&e{pPrl=S z6sbg@WCQ&Cb%+bg=3=V^R-i*pB0;!_J1w8&H%#`tekEsrcmUDuO~CNV_f)#oulJHl<^qvmf@>zyUX}$iQe5lRFN2)3FRmt>c)$(O2}M4gjqm0W17q++KhvDh96xo~Xe+$NN^tD! z?vI$(jPvf7nfJ1f6Ki1I8_rVraF~|%3efT|^g8VNFLt74-^&aH)A#$RoW7HW?xVXB z=oUqi8Xk*dIx}wC=*C${_uT-3^QQo-F4i0-C5`ngn=%*{*FnNC#z$RW;e6b_W^`Ca z`y))w?L&di^?=kj0zIPX3yQViY$SbEVweF8y~Z+dFR&~o5+#4`?bYOI(;(sw&idgh zLK8rK03hhCLs3^6V}yIE-{hoL+`zLA76rx)bn4f+Q(N!UqeDRAzJG^2sPBau^p%#E zN#QJzDiLluFdV-8IgH5JS*+8$^GQuyjmd=l&sQLow{Z2*lT4;KGIGS!xEOC?TLAj* z6ObU+V_Ysp@4P}MS;0(lI?ox4Fd!eMD<`^8e7Z$r$M8c3GJfDtkCPi+B{7>%n5A1$ zU@P-#2}IW4jp~E*NKeJs)(K89JoEKvil?(2Fp{N9VFD^&Nj(&HWx|cj^?J|-(@wUc z%1s9KikL9&B1YhE)Q`MWf6-^v|1$M**!4i2s{g6-&#u1`1Oct>S0Eoyu@KYR-;fHX zC%X6ybxLP({s2Pqj8PF%b!fajp-DihjFHN|#AL+T3AiN?Tnmv@cu}AaUT{yoY&j}D z2jyjjpl7+HXSt*&{KhM}UEsndJS8>{&ZH~W@ex2=m=`C@zTSJUYLW)^^5X>PcpeUp8Wqv1q3(dh4iz|{{JV#%!>rPZ3 zoD}&KsuI)L3=?XTLdMT(4_7pxJ+lY4C)QO;Rb9>~4{T5Hiy-%GLhq6Ua%*60BfEweDD0YTqF0fC z9=fzIg(ifZr0+on3jPKsOa6%U50{Ybfw37PqFeM>XFI#skIO6-*^&?Q~aCHj-;IL8FBE$c=fJd%g#=+)j77x6@OE zsE%J9vpij^5ibLI>MrSYw|YBw?UYqDWSHKrJP>0++M;rZ>?1iEq#jSx-^&8;nTu4F;^lGUeu* z^5i$`%{Q1?B$lZ&-_#|&F<1^!)hNC5NBo>vlCDb=RG8lLbJBa}1NEMt6W`y^Ojgqu z$&AT2{=8sQ>kf3Wg8(KPi_wF6`=N}&KOcDH|=7Nl=KcU+EI@I7mGRAbo6(&>BFy}v?EY~rEbe+u(~Wyl2Wx5Eo>lBfNw z4W@#zI;WTACG09k;l63{?02Z_D=9l|1xhP8(YFz`M61ZKE(LzsccWQl26_v1_8>p} zG?(MnES`Zp8-1~FF@b}>2(^18xZq?iigS~UYxV{UgzriSn3&*%DTUz0-!BCPD(cV9BdzXO?(8`Yi0_g8pP>NvUYXnZthTIddQrqwqRb`quq$ayd_l;!u# z-{D+hef|yZm1v6OE4}j@%;lCiYpHlVksfM=LGv0Hw|zvtMT;C?Jwx+LIFlD|evh6a zQC9=CkO|`GxD_O#EA4J@cDC=qGi+51c2{GdJ5A#nrpJLw(t>!#yoOd9I&Xl{27|V< z7zREZN}ZdE@T}uk=j{xX!{bD%2l+AnEywtFw6nMsR^Cnt9Ob1|r0l-V_B;6nkI6V+ zNF8JPBv=3;lo%`E;U)KclDx4!_#6~ohF+Mx7Oe`6z_49_ZDL1IWO{F3FM6mjo9=zU zY8lRZfGUxL6L>y{E4-ID;hRbe4v!aD_(v2lT+2Ts2X~hVW>lYak+bV(wdq#Yp=3c~ zHyvm=>?Xf>2m83skhBd${W_zJDII)yX!b91ccP_qd0^45Poj9->QKHJcy3FK-o6Xm zkEJ0QFY(rGJjp&i$I|W+$I@Q7c=Iva^>*IPs}c8T!O`~=xqqFH{12mbqQp_s<4yfT zUz*q0s27};{)Dgn5^p^1hjJVj?8Fl~|AN!S+RmUI<3JxAKp1C+g0T^!5#kVXV2!b;*Px<7M*z@m5HJF>++-?!_rYCSOf(#1}-XIB4+7n!E_Ddh|))iN&6G9k;Ay zC&(eX^o$u2OME+l1Zjk3FaCkG5htyYP4}ZiT=($u14MNQ#ns2(O@|Fc_kNbIM0p|o zTkxt|Eo_|C3y(NDw()>y_n<4Gj~2KyjH8a*_UQ|jX5b5FyOH56a5=q9+Zh_ctvHN~ zpxz#Aml3Cz%48^*pQ&UUlD)<{r?+se)3KW}!D*4hAD}7hrWM>XQTX}W&qf`)Y0NcN zVmYF_#8{!Zjc#{$BnAJw+k1xK|B^!V(M!nlD0O;x2^@i1FdMyL9C4H!)>EG)-|9-; z&B^$;NOzPR*Hd4l3Y_ zk8Q!WGRJ-UBEP&0YaXAbgwl3*^<9r9-oC%D7MnBHxq;?>PvIGS=WTa)NcR->8~f>b zUuaPYd5&c7o{_r$L!| zoXjQ?B#m9K@OogpcpHmk9-{~*16D_`Od-gvIl|Ggf*uX~cL)XYC9z3>`xs%z73m9> zWH@%;0ut)*yJHB~zo{4O(`W8OSJ-ZvNvg|bl;%J?egsF+*(heYCwZ(i4ke`Z-K4j7 zry)ni)_Gt*_XU5A8XY$ucmpfSsr#Zm#*4j$zRBz@Y(ypxR{D{Vb^t7eH-|ojrni3*4OK^7V+aOB7Mj*| zw20sx){T+)-Wk%7JT;1YqX=$k2GAVG>nGV$me2rB_wGPoCZZ4-Kg#aGH`aJzO&LGe z?e`j6GQ5QsZv+%9(eHk>6$9Nxo(SA46b148WLhXkzR`mT8W#0TE z%H*mt`>D)Qlz}UJsWT8njR#SHGw&)hw$wr21h4Zf*pY*_;Ddo~50;P70yxd^^%gv} zy7bhOh`>;AN7th0nck(WE3$usdgM5b2NcnxuJud`SQO8gdFe`O)_MO;d?D?D3usk@ z41#Y`_PEzHzV>mb_YEGxv+tuEVb}fe;m1h#CPakIL^}&`N5bKoe4jz{X2_**@9&mp=tVprX2FP z_?%cpNbY7&=LHW_Yx=(6>HK2fbXvEtBdw2S7WWncMA$WcJu!Q!T(5?z_!E+;`>(?pkz~LEn@o^dg*rNhqHzNmcqtJUdttoBojqtg zQ4(d*NwC_XDiEVC3+cF`%vOp;pxs|Zu1E{k!H~KG$iK9ZNMnBl6LwGgUu|fWAMGo{ z`e@j7Uc5z7*Fj|L^D+#w!xVVbgh>ZE=5??mlr=gTP>5<22NP#0x9H9CbM@wc8sXtukIM{%-Z>pcRid~s1A~E1~5g+s+{wI^env@U+t8WLm zLqr9T0fsQ$Qbn_?PVAPl!U8=9*sDeDzs}u>g3xi;k<-sM(94dFFC!)DdhJpGDvSgn z@!3vN0%6z7ykrGd>c>0o@7{f=n6^a$#}|ptt=vQ>kRUUPoi@xt=xW&IK#@Z1H{!`3 z?W@|({YSn}`4;w-t#>0QVmvFf{}EM%d>#YO%bhpY>0!6c(+N1HGm?h*eh% zJ)Cv8@0O_RFP9Mf!z9sjNUnKGm zSp;f7m)B^)&Y~9FBi{wRNfW)VJ_29&F9R{=u@sgWLPyJg9_Nkau~s zUigZm<1x-$oYDU9J)nwkgf}7KBc8(}TIA@snlmiPF!oS}-A}bOeDr0`inqZ^tc`W;}{?qVyEo=eXvf@pYQ2~ zFJbc0`w41cy3hqm*hTQWagO$v@Z>eFV(70rzIK43;Z>OT?^E+(v=uuF%zBP)e-BJY z8!Y4dE1@;qW+QLh5??kCUXSey!LM1`aS^xU1lqA<1+{_T=nJ6jYRT-3VtPSsIljHG z4=QE9>5o*6>4rmDHs)D0rq9kT0Y!GtKw(d`+ta=e9dXog3w@>qde_M!OIJ3dXXxfx z6rPPawA?U>IdlbVRyj~Z%44%AC`Z$w$u;!-HgUi$S|dG@OEATVSQc|#M1?_i$X-JT z5Sm9oT2lV=u}_C`vt)feX4b(1np#bQXAT=9%zdb7!6A`#Fm?Ovk-b^m zifmS#KpjQ}wqhD9*5jE53EjAkP1+*e*ior>E~Ot#+NE^_Hqjk_PNNnh!(c`&?~202 z)vBhirJ;@GhleuS(FW^JM>~0J(jhgG^Z(4lGN^+ckWk#bJ?d!W#h1~b$uf$OCfn=> zp2PAeb~N(9t28nV+@v9KIHcygMQf?@`GiI}z5$Kd0EGQ*FYM(?ukkxC?dd5s&KQ3( zKA|Pi^Jr7Z0;W5gyqEKIA^TD&MVk56Vc+Il5{P~hJ8Xoxj$edH2P|ILBnZx}RUQ z!joiw`YkRs8h$92r`zSLHMDOXovooTO7DME+AA>WuOY-hyn+D+9Lo2Fc>hE!pOe~HDXA)n|dPbx*f})a!VJFJpYPREGLglw%cht6)#V)t)RfOAjkQY z1QtsQgUIYXF$SRnyxHAfAPg|hb#dc^Gu3wIC@%@Gu z-+YoSqo`{aQU!ktV7Dn(+EzpBA;UXkgzbN^GY`m2+`Mkj_xB-0!RY77M zGy9nQ7JUKk8uD9e{@F)T!A1B^Lku6dhFQv)iah@EvWLq-RQsDMK~|*xEm}1AdZ&FZ zvhXPYY*mRn;o)wW2&qSiB3MX$MZ};;+v}!XWO9 z8G7O30N!D9W#Qsg$WC)&$XXO>klkqof9#tAJqd8=q|1>=R*h=}qmn%rh+%z%+f8d1 zSo<(;pk)n^jOf;RLnY}#_~AiG=tC&HfuqkLCv%8ZF#4Rs*`mX3DmyHpdnG@W7Q9*# zUIm$d*=_7E-F>1pr>y!&S?W_u3rn8L*pWeJW-&{kH#=s!jeYK!`@cDJ-*ac&aI;mz zE&NnTVad^q8{y)`p_UyvCHRQ90?D zI{IiR8ss)k5?{?XK8U*R2c>Z~5{{4@@&F%>_%N;;{h)Y8iP7c$=wx4N3Fbx^!@!FT zv{jwHr$jthiac(dM}7QNbSP%D7aOi0(WCKn{TuG5VQ>|O_W%vO;`|J1?C?RXUIT7vERVf(Yh zq~Yv)V7f-Izft^dpkR*@u2Z_Wn?6j zQIpF+;6aU}K@Wos#+poEbZFW~zwJwPW5=$scpUU7k?~WKdhDxT_=rd&`W=zva&(-c z7oGMGUd9a9ehU8okyMLYq2(b)`tP7be78?&E8VhK)$!K3wT#0hrsR`(Y^c_R)S z9Yj|z2p-D7X$d0>Utyd8i)aQ&@)d%P@dLh(z8eku9snqeg!005MF62!zo2)&krs7j zb7!()v%Yh7H8L5quf)H=>naMWU+@&ZxZ^?{i-g_3OT)^EY41Cn?tVRbL=SuV9V0L~ zEPU~%cgj*vN1yDRJr)I*bOt}lxV&@1M{uS=cZ>!tXjzMHsD{1tY`j(&v4=}KTN^-^ z86*{~X!-MD#Q0Df^QAsOujvW4l-19QzKcSKk#E+id>2p>fi!H@`ZAEm z%ws=~uQ9JqX~79Eh=g4a+o`Hb-PlD{kqLz9&U&ZYzrup&2f$l+h&%unUx#Wi7?p;X z+hY9*YiUVre%x$nXALn-i>0v_!uyVc3SiC?JC;d?fhFM#u88>=_HBQ!Z#J04+F!BB zzBxaBk>lhx_5vf8B9QGiJstjSx0!I@@N-(3-wqlBDlUdyg2Z&9$We7SBW3isa_40p&db&r~M+{ zVNDa}Bjk`pg+=F(X#2!mkeEDX_EGfInT{&S2)vFreb)gc-M~8WD~{ddodT2)sB4c* zQKioREuQ*d%r1vt?Fen+xw?qX2F#9eU>Y61y}@b24tWC`IPeaWZ~G5p0KP*{7%qwu zOlm^+8ZU-Rm^f%M+|B>V4!6*Ql!J{SdRUe7ZM))c`-c$7;c_MaU7*P65NJ!TEG&W+e|siv7!wtY-=T!FfjXH zhG+H*FGB(W?OZnK2sfAkHrUMHoRp|Oz(6{PIR}E3ClXAezMiN*2st|^#``tNp0du* zMi9fbq4`%j^-kI;ih&WwrNC8KbtA4K$-)rDZvMgS#=#my{6~X>+G9xd$)lyluARhF z#w*}#5O*%sQvua+^CZ~E%VCi}N4ybs9dT1GO!&I-f-uC^MF|fZ6+)9GYa7|WnyRvG z!$U$gQY|>UaTjI4QAxmDslkpXmo&AmD+mo!j^EN8ieRl1)!`W*Khce^>;;arK?GNV z(l&z5uTcU<_Jd#Tz=JKi4g=xJ`7nD5x?~chNaa}v*`wWioy+D^+pvbU3NS85ExZvG z;H}e0HOEs;i}g~RUeqS6T6mxVc#kgwS(eBLQP;0Yj^S4ZZ|6tugVad+!xS-o6S+z4 z{6asSXHNd(z7lp9CD(86Uw)Zt-uWwPvRB+qDcBU5DNpO;DnAn7dM7|I8h|3BJ?eixC=1@GW0}N=GLf2O=08~fG3sKjH zV1hmkNdY8K1ol7sqaWDo2( zIdKbZ#XO$aYC+xl&?dBEH$`BY7Liww_U*$lXtY-9#(KzhIi$I+62CzudN2WiS#>U5 zi-|rgEjb1-k<@q&dB|CV{aYB=H7K4mO#k6mgGm8@1{rxA0!H zFkH+%i|)rt=XJho_aEG)Nc^;vQdcI3!&9O`JT8}j;))`Gp1pD+MbU{XKxD`wlr>h9 ztB{KGc}VJ;q4p?#7a~X#Ts5nRFZ#X8ddpFAhOJ2V!LKl*Bn&%oMuSR+1I$D`#_IfV zPJa8-`KUVV`u1Xl>dz66@f&!oERn~4{XFy7Pl(6Hg0-F}_Q@| zhP*^_OLK209WXRpDO91ER!Wj&>9!csy~EHMv6%E9O8vI;5lI6Q+AGWH^ilQ~WE)P> zIvA1ry$Q*x3Xe9F;>|YvBRgv~*i)8~v(ItsXOz=OMn<4X650ueP(<{rdt%H{)&jyL*@21i?&s0b- z(G@j_Gdszf5R{_|ZkC$dX6y+ePKIW1{Wqh6oAfcvj*K*+1%&4Bs7rXOaAX#rPQ_+2 zKEE92`IY6Y_Pw1O6E*hk)2+j?P|pYTpN_0}5^E&fOV6K)>8#DTrwO$6E~s1|ssifI zDMFj-3aSb!3-`SO+e)Y_s4eiy-?RQO_;?LX#r}1)9Q2LRLydSO5-_kJQdd3)k(y~< z4}$vx;GmukHHD1_7)oB?y_Jy$9&#eR`;o2`5y-gWNtzwT?=|@WlNCJP6}m2&ozdd^ z#;RWW={VL;!%O!3kc10saOGf-sOzst$2fcr8mrvBw=piay`SsHjk0hwg*!&w8fq@fVgG9zS;SeH&4_w3v#1a1-Cp_8vKJ<@8yB#~G?vG)= z8(Ac;;FcH-f<*fV@R;Lf=duSkM8H}>!b$K8Vj3QgmxkSc#d8GDaA&QOeW})>D#2>- zAIOVi417R>Hvy1=jsa4OOl6%+l#*_S7*1@zlCG3W7cNP@=>d)gdE8_uj+@YFlh8Go zrRYQs3~n!R3jD|NAV@4_K2o%VJn+aYpPrBc9VL&Z#7dPUNjz>O85?gBm4_Z)u0<9y6D@Bx!9r*g3%8nYT9e9osNEpTT3&18(*Arui zpYc$O2lSc7kw;kik4ApR;u|}6bT5jN{J+v*g7|lU4?jFxge~ZC`wC7g)%#`(gYG|Nfox$gm!%z|5pI@;{HH zskmQ_~`saPGH(l;N?9xoRqBddQGjUKYw0GNhcn(_gq%sAZ>c`%%P16TST zq8+$(CLldWYpPfk845_0HinCgiSGeaT^x0N4}?(>*yp?6(NHBQ7imUNaDXnEqrIt7 z*B<~~4gj=PJp_1RQRCf}ZwtY1+l^f+;7;UdKa;lo4Ip9M!jJ3r0~WtYP1I$k;BhJ=?KUo>=U z7Akh{abmA%+V*F-@S}%`$bdJUwyB{X?DYLiv~B{m4(S*J;l;wekYIk*b>v>6zO6!1 zZwbs4k^pjb7G|#iLETt_GLFM{i1@!c$=AIPPu>xDgGBeRLUti;J_-646+L8YUu=5_iH_Z{{IK}){)3WlBVEXZ?p_&rb|{K`oQ8iUAqnic z0c3-)tVR$SE_dS0h+HePgW9gS^8FNgAHgi_kp0^LngZ}ebE7vOjhq1g6?H^IUyS5r z01`N`eZHV+*x|?pc!f%Hu>Afg6~O5Q-N5=u_D`u_qR&O`zr0`V-NLbN@LX(khvKE( z4Rp8HBgEg4WB7M3$+@X#@{4XhYuH05zq{)uW!w~zX7=rm9`3YXgBOvj(M+a{*1q`d zUjpgSrI9AIERsp>w}BGvQ%@jWpc%tZLBWZlo1cwL#nb7N<6mum(-!#>avS^m?vh9> zBP8}>?>=O4z2@yK%IUBUqpx8WM_u0n`xz%ml4z#$08&9U`cTR+kc2UNCOQw^F&>_NWx;1y*-*5DvH&ZXQ?qb z11N8nB|}58hKwG_@52p`61%ADcMv6sLti@f^WrW1c<4%)K?jQHUOkekI6t$I2IG+& zlJ7%ELiWhFKm<%yX=Vooi7^3<4!vIo>L?%cSCFK?2R39g;J0{)x`->mZjTO46C_s} z#^$N7Q5vmWqg&=b0y78sjw21NAHs+0o}K}wf9t2t-#CXz5NAcd+uWdGzRQqm6D?MS0ueHh{(^i5qPU7zBuekjdG zQV>taew$55@ARf=W+GfZ+5Ok6L=^lBG*%=^-Pe9#J_xDSEs8M0;+z3D{O4x?v;Zq0 zUF40d1P!CEUuFUX+PXk*ugRSc47_SxoIVA?*TrF>6^YFhnO=Ycda(X+?+%JZ54A^A zw*QPekJcwt-)R`j-vKQ(P-8ve_p7;gy*~S!Gx1coKNQs4{W++m3_Cz>rmE=xRA;FZ z=OPg^y7W2pyi-1h%QP8prFf}^V$iBRnqGkCf>Nhm(5*j0(@i=HsZT+qAYK&=tZwwg z3-we9?YjHuIHHeJ6g`h^gV4joR2WniKjZ*mLb%TJdyw?_I^Hpm^|7C!Ky>OecmduF zXa5B8-oHYtV#N6wp0V(#Lcojs0jTKh8M%?SC+a){`)=kLQ(G{XXBO@&`G9*%uhrV|#;I06wCxVmj4&Z=y^KJt5X z0(x>fR^M`g|M{&?qoG#4I(zybKBc?2evi1G zdSusv@>1}S@8MxkAhiqJIiU*g%zciBt{A1a@5|9spL0Cq#<{_)o!)TP5xsL%uBY%h zY{t1f3m2}nWMaCJ+U+guYJoD`H@)w5c)l<|5vPo5-3ayw1{06rJI0Sg zuG**m7v;u65A2&Ydick=Mdb3`^$r;*#>+}mUkLt0m1)M&r)RLEBXR(e2fV{&{qBe9 z8u-XMz^7=`H3FG+<2w<6)x)=+#7m3~(1uaheSk??5Z4V$Eg0_Mx11@i2_fv^2)!U( zNHFO^qk(1%0WfwSrkBu`<$C9Cs)i8ZMEApYP^~;x?B0XNNEZ#dsssk}N(}MkFvE)w zRuIp^9|M@M1Zb22JX+HXm-gf2D?RH}=?JP+TDgBk#bCcIkUk@gdgU04KE&Qs>Ias1lwB#y z>PcnAIm+$>15Grc!M*E)`bNK}#5ij7V_QSG z*j761xZ^JOuJ@Pu8$+FGxuwIN1DrJDEC`>0HTSI0)p~ojqXzVv+W*C<16M_VR1NCw z6k>4~K86#VzPK_y1s5+ZZl++I{rX8U1V z3HBu7v2O^{X5isrSaYHztbbD2zx_f&8;Fz`WWc1EXgD5yb7&y5bQtdGv7i1C4fu6H zgP@mmX8ZBzc=QjW9PQtb3FS!m6_`sWiHB4IV9&sl`}BS-IKTa{20MO(5a?{Ycn?pQ-=-cfnR2qE z@MH-75J1Z9S=1BCZqGiBw?&=jA4EW%jDuRdMge(s`eHkL3c429e~la%LShr1=+uu> zh}wVQfB(w=t_Ld7sh9J=i}>F>{#WCFNAkaE{O@VxKX&YX+x}zNX2fF~wj<5QdK%pV z02OH+eaVI?HGuXj?)Ep&V7HI^w7^9I&jrtO!;w=~U7q&QNpI?N*re#4@|>r7|K-Lg za=bV(tdjrbbcrq15gg`t5Mg^yzc=-;w*b{Vb#?ERA3#S;y_!)k0%KSc8>?vdTyh4lja~51B?4T%zEqUGvfLUD)GebR!+K{QvT= zVH^4if0b=A+@`{>sqkJE9#-LND(qL`cUAd4D)g%KRVuzng%_$YONAyeyHxz73eTvp z{|1TATPl1_g=V??Rs0SWZdc(|DlAfAo(eS;j#8lsXX%$@J?m9itHSLnyiJ97sqg_6 zKCZ$SRrt0FKUQH@i-bE)g>zN7RE29*c)bdDsPGOI{y>Efs_=jcUsK`RD*RZ5Spij_ z3iDN1q{6i-tW{x9g?m)^Z594hg$Gplq6+&}m==`vj8@@=DlAgrDiyY<@D>$*TZQ+l z@URM>SK%8fJfp&lkgO+*HXGwFU&Sv|;R+RQP+_wQzpTQ&D!faD2UYln3eTu;uIevs zi>xP4g-cas7czg}18k zP8B|&!hS!XqktONAe*Fhj}rg)01@ zN!It83XiDpK^6YH3b&~6Iu&|Vn6JVKDjcrDzcfm?r&Rd73V)%(e^p_J3R_gTUWFwp zoToxfg()h0ce8~5iV8JTZdLdf#dkd_JgCA4RrmuH?p5Iq71pS*LWP&9FjMh^rs9(o z{~cEKoBHS%<5PB$onwa06J>mFQfTHcQ|V^>e=6+SC+n@;FGCY9hWdoR=C+!u#$bK* zHPsDOfq-vIRYQGERj{cgkXOw`&i%f*jWYOr^^NsGpU;GE=Fb}dZ>Ys&Ia!v^-u&KV;*VO2%t*UPbq5bE^H?cf74j_CWT60y4---i4 zJ~y1fOPo7@tlyWlG=-WIIv@3H^jn%weR$4(NrID9u(4`0lUDU}Q;RRu2;RU5PxY7?0&KwFP)pGHA63mi4ga5oqsseRgDq9niL@oLP~sp4@@ZWaF{)M-47Sv7 z2nGGxw5fRo`GIK?;JMp1SWQBhsfk<{_>BHNR|#2vePf-!1&SsnovI<{#%u5ro2xeY zebkjL_0@4;pT&3QDxv5C@rlFvtoD(hS#!$r1992Lrqn|XV7`@ z@_A-K2}a21yw3|~o&uxbT>N?AVD(b|NqjE(yl^f_gmdZVg>z{locW&@&iq6;3qCKL z1&MIxkRzS{&Mg<8+5a&BlHPOX{Il>(0E5)Jp(;=>jSN)s58-Pmsqmcpx$tB-iu314 z5DH$wKMzlWQ1D{%@Q=6C#7}}y@M3cD`Qb?r3SLY;K0iDOLcxp4$>)bBK`3}JdHMYC zBnSmBCO4lSo&+J`&Ha3GXXQTyVq;Yh1Dy}%ZQ@vQZafDsaqj#+UvpclFHqgo?DqwN zEg={Q@v1&c4hNQ-!&1$r?>|)ETwkJDU<%u3>w$sw7xk8~s`hBGo~EjW$~g)?v1?xv3`9 z;Mc0F8nvdzhBj@3Uu$TpszHYO#$XfD{PYlm(hNw=Elt(_Kp>CigPga8W`y!d*6hRF ztHCeliA^o_8|xd*^yaGSO;sD|70p8$gYooWU3^{{2yKvf`2zl`7R>P)nyRaU^-Yb+ z4m8_WCXt_(-2Q1A*|${BX{-FRX%<_L8ESxmnv=Fbs9EJP z;cp1lHw5eBwN|$U>oEOl31JdVGZ3H8%pq|TiYh-_RyZ@=VQ}o1Y(;K0^z_01jwV^XB89Z8chRQveNb zYOHF|s#-RNHlqWk=FJIc)9K~L`Yry(8I0*vD!UQ$*v%odzZv`iOFRTfwM{MBCV$)3 zrk0u*1V&gkFjlY11NI69g2YqMJ@t(nWB8fy!F3^;PD_^j49F6GZHu2!X{o2VHR=sa z(^~wselSus7GaS5-bsy^UfNEFcuVok2nXj10i z0FKvCl3WEcw-8{dU=?uI{H@jgX6g}bb5$FA4(h75pb1)l=IK?KmoeeZawfu4S7`wt z)ldUIM|KE!s0D1Ift)eWO|{yRs(`=LtYH>|pSp@*DSYF#vjABzhXBf?WGE67pAv+8 z-`oYhg1KUK9fY|?E0$CY!XW~lBNjCI7_^{7=_C=Kk)PxiW1_aTrHQ(%ew%+5$k>J^ zRyS>KuBYw-3O*l6iq99rIN%Rz&{m)u^O;$Z&-bNJRm0zxms(aEYOJQ-m=@4#>iMY( zb(>uSHm>nEK!3*M5!FIHY}5uX#|SkxHO?kjOgNtp0$l~J3JivOFhr7O(ufN9e4Dm@ zRtW-ZE#UK2HNyQQRpnq+Bo!dMeKk!nyBzp6RYR3=y6I$LJR}Xd5gd=xmC}6B(+#z8 z{Vm~|t)}->ank#Z4NV)WX!c9*RYYas@+7z9)HU#;Xj7XnLp?G5C-p8lSd^Mg)UiqV zl=|ku)+{hUClei(RMoK3(rO#3HbS@>HJ`5>_FZw?QiTpHr97>yv_ki-^p?0+En6*R z5TiPV{}wGa$W0m)-((dmHFl_RLsO`+M%(O%F07HVy&6MAqu*bHakeqmOhOWWN+1Fr!RaJ#;$cEJ2AN-L0sLp@7wm9QW%R ztD9P&FrX4h=}eD}FEZ0C9@*rx9PUJ{c%l~RLk&X;Xp1$t zc*z?%U5Q>^eIQobEN$B07zC!x;&F9GW__&|2L^_s(UwMCcE*Eu(`RrhG=^5)=G)p*)jXYSSRZjS1|tw;G5s?OZ`m4*p(&fPI0jCErz#XM zOPi*H)n+VILpMX^6q4z|;Zkd2cyL}ulb{1l3*@|W&^9C8efv?G$h@!LTAk}GMWYc$qqQ&%G$)&TPa2o?8K^Xo*rl7Zl#sibBD z3#XbA5bK)QT)^MzuO`kWD}w8t7DS&zwb3}uOv8dFjfv~Qth2#6G;aWZ!i`M4k8-hR z4VEUvwnkfKzAbI74~k%2OVd{516K~-aMLcL8YL6L$XOVN0^*uPjX~y)kS3evHvRUc zWR)(2palZ*eFGVyu`+or{*CnkOyADg0TvV!rT!c!7MrTUf?_P~A3!lyW*~}*co2_N zH#IaspF>jx@~SpepHttMt!1NKODd)G5cNCR3@`m9VPD44kfwKiIh1P4gIfsnFst*s6=HG4pKw#6`2=0E0(>Chfm&qDUA zX9YK0do9-`%f#(*Xn}vWIR+(`Jkd>vmcZvLrt&6AhykTPEpQRKTm`zw=POZtCY8vcv4&kTCySByO5&%PK&>5&l4P6+p=V010 zO{;0bY?lU)%{+vu8Na0vqhPKuuQgY@X-cS!1!z4%Ym#wyTR^xQ+lp!By^QTB@U`kA zxM-}|#N^n*_GDOBHQMB@bxjTa$>8Y5+6Ij2voLOKZoq#Xv`4>!IONYk7O|#L8VKh4Ix$DG zL$C5ex$nSS)dO>@jRlzN`Gcs(TzK}0EmSXekabPYCmmVzj>cJp^fybJOcRUA+!Js; zZLSZJD#jeKw!Q_91=Co@XfCJ28=(BA!^bvDSx7VTL@lfvnoGj~MUR6%TTu!jdEzi{ zX=tL!n5<63@s0!3s1v0^k=@NbGQduj)9VeG2j^*}c^mUk41O*Pn98CU^y$+UF({tJ zUIqcl%gdWl2^`3AQH8lYF?dyTGixQ-MIy#uUMTYtIj(MZeVX7h{{!mMZ z%KMhCSwWw72IWN=gsx#_O9|1H-=qDrwB&xwTSRWQhQMWVFGN!_<{JTjv8spmM}S|d zz*S5xZUNvyo3VZX?&iLWg!OoH9U6`rv4uOA=$<@(l=4TAAtUEpPsThfVFv}Y2@!!2 zGS}x7c?d5*Au4sX5DU>>>pYZ23h&vk1A5S3k>X=WacsW{r6=zvp<$cSV?CNkx_E!V zk03pGl7Zqg6Uj=16aDc(5#KHXJY$fpO&qK~k|c9G3hk5e#SA61iQ*=|Q#}x}1Vt08 zg#jpoD{X7PoTd|-IOd5WIxpS!F}-D`fFy97-?3$Hod-)RIneD$|@RR zW)Z2#H=->C5Bg`&s1$_%mhf6@>Jz!EWWYMWAo_$I?W`qykaq%tKk#A;=1|h*n zxW>jjljLM6HI}oBmF%0VQeHlmrG!=egu*BiQ*#!=nmUyrLU!t#dDbA|7uP|Y%Bq@W z3_853g-8*jFZNEc+uBr67K?_9oJ;SS>JM`i7LZxcmuS=s=u=<@NA6@i$yaKC;rDnZ z|7k;m)}$J)ECH)u$VxvN-)aAqxz{PFpJH3TV&srwfH^SN7tjLm{PKV=DNq9sSUvHJ z7B68s5wyv$yU(#+X67;Jn7mJltOnqymG}-&obZg z(kt;6{2l9;r0qyC`_Y6yaDScLKSy{3FqLOy0MpaR?Zl*9>3#NpkdCtu0Qr*Wn#})k z?USGlNXJ{pMbs6-w3P#lkpBa$k%U(=S%ln`HBfOdoh4bB?yJrAb4-Y0=31WHaEi|t z^am{dLo`9!7fL&-V*fCm&PBL$umJRL3y;}meI~g}l%9j7v`&h!%%M#ut!EuQ7PE5G zFzae-3QdEwDV0kGG)WVi#25_ySfPR8M@w&wG`pa+x*75IC2g!5oR4Bu(g+)iStYOz zl(4y~f;`X211AwL)nS~6H%6|i(HNT8&lp$Og0hT%F?lx4FrJZ0Q!TcXp?vdH_#d>9 zd~?W3vRX~PxmLR=k zjxgda9?ck~Q4zcgvGHZ_aVgGqi{yp^avk&Exptqy50WsB4+59%=W{`hO(#%`>6;)^ zQdJx8-+)XFHP-Vy?Vq*3|3{S#keyselEdrPur5sDK<{JurxN3qX|ye_XbJf%EqtpQ zT11<*|1bPy>`dHuZC0^&s!YFAg=Tsz{J(@x41V&%n!)kBL*aLi3h!6p!z%1jA%WYr zXf#jH=)l@~Y|F1%lOk$7a({kub^p`x8~{uKt#eT4EIgMW)DYe@Dn)#PpVO5hx<{vo zm+<@H*c9>a<5I*i{Q4%Oh<^MQo)0{oxSm3!h%P67Swb8@T03C;>%P6<*3y zIXp$=1u>(|-OT!-67Ol(lbM#ApC2nD(^`elQp8?6^3dp1mCKv1zypGIBjx4iSJ%Q2 zJ&ZWrd5~AU3P-|=-FOto6Vq_YS%{MfX&%nklbFW&A_-~boUcDIjq{yJNUPv{;zH02 zbxB+*IG+<~{jmG;TBr?pN&QQCIG($usUf(T;?(cgfSf9Q+qP{r8_3S?TBq9A60FYk zxVfIg329exJ;$wS6*Aw+gfyA22WdMUsHaupQ@Jrkkineim+|9>ms4EQoHzDqbt!^) zMWq$sQOrl0rElg@8lC5uJ3N+8Zfe_^BJN*;G}Zk3_oayGqY>W_koI3sikL@bnpKAW z6j2FSc`bpK>fC(Z0$GW)JK|}K6QY0fZ|p_G#DsYh#01+Y;p`vYlXX0^E2DWxWjY#o z417v3ZMc>tb6&d0oSWV}b^@MlV?=6mN+rGDiT9J1Wr<}UjueYdohRlzKT1qK;1Xjh zMvIJj=Zi|T<8l0)5Zp<~n;3^MpqxNCfpP-n1j>n|a#UxwZG=egO3SA@*HfLdvc#+p zv&8wQvc=fvhl`OxhZt7l6lo~$91A-XzYbNWHEwgH3rEdR;i$+Ej=95{C)rbk9c{}8 zavShFPGuZY_~ly1F{#)_KuyBDMrm3D@L72>?L|%N%Y9g z61nG~hX6NDz|W+IH7;p;o-k?KO0cG8iK!nt#l%x1#JJ~2imc#Rkx`Q)X}eH}OYrMa zxH~OzTTZ6PK_BE)I7AK@Hs{t+{Udvv&9##9PPp=k$#3 z8hZip%vh0rJdOF3_;}L#EV2HSOO#dQh>0J9-w%uynZYsWlL?}$LWsBUdzkBnPO-#g zyU@0Zp=eiz$eF9!H6cq(0PYij`-FL!V#3^<{?R?7j*sj@n@4Ag(G^)@^t@qW^xTpC z&Yt1Nvzmuh+A>l_#+TFjQ+j|8^Io1J-iti1asB8Uq*IyXv;C(TWy{Md=mHxCT!AKEjd z%if$;nOdF_(`Eeb#K3oTz**94C}@@enhgQX(#5#BqkG0)kRhD&#t7$$tp3cNxUQfw z@|{aKA4q&hW7cQs5iWaSrWga>8S}wFJ=0A1jLLv6!wnN_S~nzJjj%B8*<34JvJ`U?8^0i~~88PK)EgsUP` zxaJKNu2my?SO*0&#GQw2qUb4`IL5e$gt*O-hBoqeSt;|Ui#w0l#8pSp-b&S%&{ftn z$tM{1D!?oAz$@SrIj*LQE|lxXZ$6iWu1$zT#!e;Xu`Nm$i$LQ{=t8nVnC^h<0NmIZ zUW0M?1)KP{7j5D;1=kw4In#v``rClfhO^#_~y0KfbVidP1tfd?#kB=2N_cZPv?VE42@$4Hqg=e7X;&Oh43WBBbMn8VV< zFu)Ya>D=xNG2&gD(D1vP>lXv!R#>(nsba_mvTo7WOv_B+e9tDv;MY&(UFpL0VYaR}DWkOxO=(a{ASHcZ;f@^2Osch7C<9Z{v32b{@=UN8PIA z6l2)lKZ9oYy+d%8XNl#gV<~Jm_w$fP@No74lAjFD^CRT@P}N6etnmamBVp47VVj|^ z$nM_zu}xHdViU7i@1PIyj{1>!KJgvq(3ZTmj5LukH?29f5_*{DCsH>^{zw<)QJdJ0 z-`zE`53F&UJ6*U@$HIzq*5h0z2wAhGh&SOli2D9WD zJ3dtmwdD%pJ+h&kP;vNQjG%D@JmdlmUFav*+z~xH@g5L`J|aatgdh1f=x^M}6fqS) zGd_a*3c6}ldNY+ltAr2lci{IERi=XS<9ATSzlZ00E$=D*Bg^}tum#3pUNx=-)_3!H zl?0dY{2lTfIxj`M0yq@14q25_ZX0S7LpPu;#6vBLhqA$w^T5+{)60j_I6hej6HYWI zMRdZqxf96!!GPBAwzqw}dS)4|E?nteM(Rc+ek(Tn@BM{-X;M zDD77G6*wQBDcpnjE%@!lum4(`cn~4&B7i5@vcQp7-CS_#?5$YntJ++1$-F$Q2#NAG z58axOoE4?#*UNsvqqwXM&y(OQf}g_tdte)$wG{ENB`xI!%=3}fZ%KO;X%8dqTz~(2 zZ;+(T|K1xU>F~ez21z>n@4Z2i4*z>^kfg)^t9yeEq?EU<_J1iv=hl3@;{;j2(n}coQ=PJ)KIzSjZZ&pSdC$aMS$27oJ&7l1n9=S~4q)G6z5FhnAA zD{5e_#JD^@b8@*lBu^txDw7D%3y(sz_$1%2+C z8gT|WiL(W1ZM_Cd6puT7tcKKF;-p7)>M{ zALhDw^Ni}WFC#x4B9Qq<#M|U)EN_5VP}9n&H?Wa8C^Nz42d}eGQ%9S!`yO&B3E;~1 zAu%#cQK9x_e3d+|py0<9r)dc@zy-VxMCz_9jR&&WN>4 zvA<|XKBae%B=M8GSU)DrB))hf)2@xvPZ9V!O9~E1i=Qg`4^YLz7taZKE%7%9OuzfAzI^aRy3iPYc1_}2i8<9oiD^)mNZgpCPzGnvJfMd zw!4Vh*o(pu5sK3S9jFwrSU@zRK3GKu`f)BOUN`7M@?=WmH%a3|Q$pe6NXL&AC5j1k z@h!6^x7=F2)q-6K&KJ{h(sj&La&!~o`-m}LE14JKyJiXrrPyzQz|11b*IB(H?+u~zz0e+`T{AOqc*yoDHpVsqdh(%N0nItGeDM^MZChg zyqwQW{uOZ_ZRFhyY-j+$E&%X|sazFDy{u{~lNG_cBI(QIR{Ag7Ok*2CCbl4r*iH8S z%dDjcF{Pvg))U(xNf;aH1gG3*H$%Z$-G&X$n&Uy$QX}!jH6j!#}uhpNVrmA zM)N7;jE%->qvAb-)@yQH3e_$-m<$FI;BvB^<<9GZOY>@I%=&ZMs$fHLRWlv8ph2ES z(N8H&qHLO)rHwe5Cq$YZZ`Rhg1aU%XO(Xm}7&qUtuW7__-5MNrjj7tDxG)6fow(bM z?l|Y`xhcS_m<+$hV7a_aMI5kK&;b{m|L~hh;+|AbKt-BbmiqZrKBUa#4lskY1_~}W^vz162LT2{X zZ)m|eV(PqrxH*+{0o|ao&A*zIw@w@N?*z6b{tcmxIQnd2xx94Mic)XETuJi`?5HgE zsZHP2K1n=KYbw$ML3tJ!TQh5$a8E)Y*wWPIYv5gqmx;}|%#hBvtSw%(W<`aktaQ%2 zd6Sq428px zPzAK#%C9#5GT?=L%u1y!q&rq{_?tKSPY>WK8k`l8HCn(`gPpKTxT( zSD?;t^PW#tDUxE_+W9&JKu~wTP$hKKyQZpzPh`qn;y4xT$B`1PsuqkOPj@R^V;f@^ z^3HoYAK2h;Rp<3l3_ORO^2CGz%^$4Jn-RmyN&}+Vmiii~1`YRv5W56$&XUpR^Zax`G!_1% zK}=q*v+az_6xKs4Wjq;PbBU}UD?$9%p~4s(72m1ynRu2b<=d;?cO~V6GT^^tcq;jC z223#bmZtgyVc?4xVn=P{SzB-v4yWPX9d+9gF(B{WHYEs0^ywZaIvKhZb4(mdK#2yh zdP5u6BBgyJAA=`;sSh;bXg$fJxlJ3zaa&P+Q>X<;y>Q1)P=gl4TTLB_B5Ct$QwrwH zok3C$Ubb)*aWl{U@~lj-zF6&Nw#m@cew8Zy9=A;YKkZ!)kmFT-e|x|fDDAtG7-wU7ets`biC{B}u!$4cdyn<(Eokfh0Jx``+zt zS1I}Z6db6;scT%0anHMFg17}-n!5vE!DR`^z>xtXI&8E!$R zTS8*@W}D!vU#$;Mij!g-2e9s#gG$?jB8ObXf;D5G?Ak=n_w0AaH8$qaBX|1L+j$ZY zcR-WJj^N=S(9z?~BL>Paz6Cov{uuvV%P-CQnJctkw@%mCBep&Mog<&ae&T14-@~V_ zXM23#^y6o}d3~Z7XTP1tw0z;OO^&n46XZ*gFU@uumiCNZ*5WgIG5QZvKVJC?9{qXW zH?Pa&YngT)pDb_l1o_%VUQ0Ws-|h+W^~slAqrKq?^7-h$&Km6nPLMBT_`a_779<)u z4Mt9oFGjxf6S}>DrM(3CqEBkR7Wqwo>qV24!8V`E(|VpYJPi+?n(QwgHr48$7+>(4 zC&s6uW@Xep;Tb)5e}rfHTr~ZM4B^4ShqOHYKi1gEY4oru$20D*mtlinw=UA-%08i7 zxlZ!*XR}xCQvErv{NZcH_2X%7<6e5~HQ+ds*S=(`Z>us;h{_^ZU!q=v~>?It}xmtd&KdraFBfOtNoi%y+LW58r@%``iqg*PkXdJ zHQ64vc+z1!2^dErX0O|rdF1Kl_?Mn|;=f*xBTPL00~=hkL3*&+BS3pZJz_<7{c6}F zekI3o)r&Kp#`MPY+Y0IV7`|ol{PA7P`0$O%@3*$;_oF)|&*%6K{eJQ%lRuyMvVOnE z^(1Q68KT4bq-UK;a9ru{Om4TuIF9p|V{t8-r9AYr^~SHfiIi_y@Hdw)xb#Ouz4qZ_yh!#*fT+t@tYz-&(sjMh_1C*6Xi*u2Y@Q zPqwqso0$*h^C8cEr$vA8Hzv1Z^M??BKggMmh|%8|pAVk5H|*t<#~++iCyyiQ8A19a z&FB9Ujd_0G3Fjt%-t+Na+Dn`(aOZ22{;-x^&AxMR>G(W112%C2mkEr;GxNT|0^?+t z{hE4%>y3Bo<+u5t>EB>r=VW{NzN+h!w5#E_p{>6-_Y*A~T8A5_1X^|8nGHo9eUdl~+opdOn%FUHyIw2G*L!|)e+BdX z_kEM+ahdy;@N<(t@8i79a2}huVcQ>h*M~m)@8u`AZ}ia$kH4R1JYDa6UoXG!F+IPZ zj%ghK)eF8lrDt@6aUgz^=EKbyu$|=iiNL1 zKif`uaT0O-k8&ibm&EIIdvZ6?*dO@<^xw|Sn(u-!CVkFJs$!Hk5Z8RKvhcOoZCKH^KHI)C;dsKQ>Cr+idIpY<$MAf^08GIlT5T>sWz$NppNQ zd9$={oBm?s>(jnT+T#_+B+wM)h_K&P+SfKOn`VE(erge@^5$>U`Zmw&7(S!FGTTegugvoRhR?*8G}~LHp1t-ke2K?&fA?~Hw)Vp8H^q60 zn|;RrC`Xt5hJR1DYtvtx{T3M4UN+v^(RrR{dp16!=N|n&L%rDIWP|?f?9t=%et$H2 zNm1S)<-Nu-z7WSZyjizr@>d>Zr(UA;t0C+VrQW zXB(fxbz#PdRR-yf!_@ zsJ}k-XItL}w41Yr|E1aQ7PjY&yJo%Y(7x03dz+t{|5yDm+q3aC*`AHh=r2G&$lR#? z)HW|e)N`JCw&^cS{e^GR?b+;`O%~tPty-<_AtNu25#1Rv9*_^J+hS7roSP_r`R5Di5&mqylm4RZH^DuK#u>B zFGD^b=cRWaVeFA5Ux4;~`FLwb_nkfJr9u5&XyFS}jy(5?w)m2v9~@aDJ_k9z9Q9(` z9}L^t4nvO5wm;~zJ;nX_4v!orzN9F}fO@gDm!qGy*5DgZ-sFvXoVN8X&-P+#_-TRd zWyxpLbB}Ta7+-9D+NEAr*N8)LjxS1iZT{S6zgy|2wm8|M9QV_{w*63(?OkWtUmcz2 zlq0)_yjix_qyB7uALIB^j6=3~m8M?0?APWmZT1_dUTk&?u)W9{dMQxe^cwyWV!ti6 zXS3U&t^G5_@!9+(Nxej951YJxj_)q&#kPORGQPy9XWM=^K{ZM8j+4j3l>cwGuws_E?98ucM_Ipl-ayaBW#}n81bEoRkzHREy z_B|j&c^_Fr&oTD9bGP;z+diVjesk1|P0vy4Pq96(yvA;6jxWyfoqe3VI+FXycQ)Iz z#g_=@n~l%(+o0VBl;b*2zh?c&vb{9d-#@ePB`9x!cC(F7v0wilt>@b~|;aVdIOj--oDY+dPWV9x0B`yM7qG_^6i#<+c4D z=HvJp+^@wgevqTQ4aP~E-MW;cxQ4tD%5jME$hJS|aC|N5#VfDb$Ck+#zft?M&Hqy5 z>(g%D-))Wl8q}YUda-?v3e!*XT(>@G(O-vp$+KVEenXK@QI1!7+B5R@$k*JX!F^_@#JCNI+R3@q3PdX*7Cesc!QqPGW}Z7^Ss83 z{XA|XwlUAGgfG(e?X}14Z}ejJOXmBBsW)iitl1YtIM2=dHhaft?}%X}4sO^OIX&GR z|I%Y`bDraP#$QfNa)b0R*rDz1KV|asG&XzrX|JeP{MY?z*sJqmjf46~Z_so4X8xIV z+pGRxzy1Yl02|rb7UX+DPNW9#&G_NSOgRQ6+JczOfwBQist)NG9 z@JMc+Q(mQ10ri5Cd#43@GzZT<)R|m6g6}v_sUzSKd=SNl{4;{9C;`wd;J=|rP94Dz zez=?lEqE155;UH9Q=bXrRvzI8{@_yFu?%`NACBb7`QGJ9l~FHv2J+iydj1^k|+M$!YV{>!B0W3;qvE2DISjHz@UC(0JZmoqvn| zEY2RNzrR(fhfzP8heq<##FMy-8})+kM>zsI54?FF?1J1Zao|_>V?3Zcz-!-$JD;T< zXmZSq=8ln^GKUXBC*To$$&6ADffhUmr3g9$RqW@=HKFab)kW`HWQ2!7u1LMblUx)e!flOFhoU zsTY28%9U4bo>GTiHl+%v?*MmgLEZziW@qERwtByvvZk0__W4h0+Br_$HLYpwqyGX^amv&IqY1cTK4+$Sr_# zQ|j6)aj!dK2+l64=TXi^y}D{jof*|Ucm`N~f;66sROK^qah6U^U9b6Zj!hjvk-P_3E7ea?T>jTh zsjs0(n^?ou8*gBn!2d)M9_$HKDW-X_4^|iM*7f*qpgxHrpT&6&b@`3D9%o$CKccv8 z0^fO)rqjTWzhz48M4Jxqwm;PTN#HZLX`ay>HIl35q2!c`fnV^`C@Ijmt47`OIppF1 zodkXk#qA3?I5edk)C=DCPg<5N@X9YBcLjVS3S9gmY!99W@Gp^vM*L?qAC2Ut`24Hb_<0u2rJ>V<9scnR_{OVcK;eVY{ z=OYJ>tU+Pm!zk|ffhISO;HBTeXZIopBJehpFz6)kw|Z<7Xma2Pe&oAT>aE}r{P_2# zR0gzQALSv?qd99v^VvwAn+H)2fk$uwr3+f{^C*4L9pIs75Z9!gf1gsH`VZ(~AM^<{ z`Dz5;-$#xn)C+zdB@SBfDbj+6NegcN{*<}{JbvKS|23uV1sw+-{DJoQ(R?$K!$$Jc z_>S7^@1Nl>3|mebCe$FJaF?5^;{dxAv2m^W;7R#2^dNg;;XkHn~FZ1Ie)+gj@5j^w9@FmcKJ5Zva zNAtx?1?snkbM9MCvprhezNO|I>t zz~4sh7m*id;8YPsV!~)Xm@lAi2Duzp@mayYLFs}P{5zDxpmE=iy7~-^AD_jYKI)UC zTfoba-_~s(=-;e)1o!wisTX8Cz|W%i_rsS2kw-?_5xfAoT(+P-1gxRNK?{Bxd0mR2 zd%$y$zexNr2)tEt8G#nO06BV}l+OY`hVm`Y4+CS!+1m##_)Zk*s{s5o=~dtlNDqKp zU#@usFDE?>yo0pheWdRPehWp`GQraW`m=&T(t-z2WSn;ae?)o+%%8PM9l_WHS5bsV z@Y|#Zz}LPK@**e9G%$lAx)od{-2~oo4*CU;1tchskW2T`J+^FYbd z69X+M`FJG1mZ0R~5n534=?E<-IdLAvI0Zu}TkzRm|NURpz?7TgIDoGo;}Rlvqar+^ z{?wE4eFN`Fe&PqmKjglezAAI~T|r#&H-p>Ec5S=l;_$X0?pVV;bnkk{u5Gx}`=ZD; zUN?6a?or#d?Y=u_wq14Q8K+Gn3(}o;@56;}Xy9ElySBai;JdDv!G&*k@142m?gO_U zyl3W~{r6skyV9=MJ9GEN@3~}K5Leb7*pFLYe}A+$X=;aQ3oj;&@&9#IQ0KMImkK4P zR4z42tx{kixS*nlX;4gIAz8s00Cl#|^NsoDe6*M-CX1Iy<(C>u&86;A zZ%I{sRev>94Ob)8cr{T?Rx{OXHCOFdRV`GD*K)Odtyyc;+O=LSxtv-qEH{=TE76ta zioYJH2kW7FxE`&?>dAVlo~~!=*?PWSs5|vWy;<+nyY*hZUmw&VXdZikDWKzXf=-3y~Gj#m3f!XkEWHve*n~l#VXH&D;+1zY? zwlM3=HfEc%{aH1ao6FDnTpb3A!D68}C=QFFN716_FkQ-&vZY)}EG2eompY|hX;2!L z)Pirp51WM+q6@Kw_(EbKxsYDSEaVpo3+09OLT@3pm|pBJ8r_TD^JS-8hUN$53^ZJ> zG%C$XY$?8!a5bA*$}Sa_oTb)M8@lZ;4VH$`u)i9p23;MeT-~;-y=tn~uSJ(*%c9ft za%MTZoOgBGT<$D)mj}zkWwqj4@vj6|LeO?>CApGX$*$y9@+*ZEXQjN-SZS@aS9&Y` zmEnr2`|8>n;d;ciM%=YW4z>_Gh!vEB{Q&hJa>B0eGfvjkx@cUq9heQenoiB8T^%=P zTeCgr*f-~&3(rO75_8G9Ec9KTYs_`#x^u%hH6NT0&Bx~B^J!S1Fz?K_=G*iA`N6!u z7$`=HQP<;h#eA_*Y!EEcm1HGV$yM^M zow}7?MJ@T3LQ7%SVqz_4sSI0nTuX_SqSaV6RZUm()k3vdZNXCgs;}m+g=>*oqL!>> zF(b;gMy*rp)`m5;99#~;igDM5g=J^CwcK9r!+!piz>3Thv71<}ff>_Nwm>YS_j3#D9&*?k9S-(3Q5|{~D%!4xKKxei) zJH#vq&V}Y;bMd+KTxPB?=ghU{+H?K6!JL0SFdvzZ&LhxMjBQ06+^{vF^;H| zDQ1gKv0Q8yJMQcWl!B!wB2Wr*M`leEv8`9?mt?kt7b5OF$u8tDM;Z&Ah3>*|K`jOs zLyNJ+IOa=cv9RbYwier%IfF%iIp9VwiCi*!8s(-Nw-lmQ2r(;;h?POSaxj-9T1l)5 zECms%Vu(~}M5+QJRm+`C5~;$7Qxc`Jh*9NgquQxD30X zk#E_*99Rx7N0y_mr81al0nDqy$^dgm=8c{;Da0 Callable[[AnyCallable], 'AnyClassMethod']: + """ + Decorate methods on the class indicating that they should be used to validate fields + :param fields: which field(s) the method should be called on + :param pre: whether or not this validator should be called before the standard validators (else after) + :param each_item: for complex objects (sets, lists etc.) whether to validate individual elements rather than the + whole object + :param always: whether this method and other validators should be called even if the value is missing + :param check_fields: whether to check that the fields actually exist on the model + :param allow_reuse: whether to track and raise an error if another validator refers to the decorated function + """ + if not fields: + raise ConfigError('validator with no fields specified') + elif isinstance(fields[0], FunctionType): + raise ConfigError( + "validators should be used with fields and keyword arguments, not bare. " # noqa: Q000 + "E.g. usage should be `@validator('', ...)`" + ) + elif not all(isinstance(field, str) for field in fields): + raise ConfigError( + "validator fields should be passed as separate string args. " # noqa: Q000 + "E.g. usage should be `@validator('', '', ...)`" + ) + + if whole is not None: + warnings.warn( + 'The "whole" keyword argument is deprecated, use "each_item" (inverse meaning, default False) instead', + DeprecationWarning, + ) + assert each_item is False, '"each_item" and "whole" conflict, remove "whole"' + each_item = not whole + + def dec(f: AnyCallable) -> 'AnyClassMethod': + f_cls = _prepare_validator(f, allow_reuse) + setattr( + f_cls, + VALIDATOR_CONFIG_KEY, + ( + fields, + Validator(func=f_cls.__func__, pre=pre, each_item=each_item, always=always, check_fields=check_fields), + ), + ) + return f_cls + + return dec + + +@overload +def root_validator(_func: AnyCallable) -> 'AnyClassMethod': + ... + + +@overload +def root_validator( + *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False +) -> Callable[[AnyCallable], 'AnyClassMethod']: + ... + + +def root_validator( + _func: Optional[AnyCallable] = None, *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False +) -> Union['AnyClassMethod', Callable[[AnyCallable], 'AnyClassMethod']]: + """ + Decorate methods on a model indicating that they should be used to validate (and perhaps modify) data either + before or after standard model parsing/validation is performed. + """ + if _func: + f_cls = _prepare_validator(_func, allow_reuse) + setattr( + f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure) + ) + return f_cls + + def dec(f: AnyCallable) -> 'AnyClassMethod': + f_cls = _prepare_validator(f, allow_reuse) + setattr( + f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure) + ) + return f_cls + + return dec + + +def _prepare_validator(function: AnyCallable, allow_reuse: bool) -> 'AnyClassMethod': + """ + Avoid validators with duplicated names since without this, validators can be overwritten silently + which generally isn't the intended behaviour, don't run in ipython (see #312) or if allow_reuse is False. + """ + f_cls = function if isinstance(function, classmethod) else classmethod(function) + if not in_ipython() and not allow_reuse: + ref = f_cls.__func__.__module__ + '.' + f_cls.__func__.__qualname__ + if ref in _FUNCS: + raise ConfigError(f'duplicate validator function "{ref}"; if this is intended, set `allow_reuse=True`') + _FUNCS.add(ref) + return f_cls + + +class ValidatorGroup: + def __init__(self, validators: 'ValidatorListDict') -> None: + self.validators = validators + self.used_validators = {'*'} + + def get_validators(self, name: str) -> Optional[Dict[str, Validator]]: + self.used_validators.add(name) + validators = self.validators.get(name, []) + if name != ROOT_KEY: + validators += self.validators.get('*', []) + if validators: + return {v.func.__name__: v for v in validators} + else: + return None + + def check_for_unused(self) -> None: + unused_validators = set( + chain.from_iterable( + (v.func.__name__ for v in self.validators[f] if v.check_fields) + for f in (self.validators.keys() - self.used_validators) + ) + ) + if unused_validators: + fn = ', '.join(unused_validators) + raise ConfigError( + f"Validators defined with incorrect fields: {fn} " # noqa: Q000 + f"(use check_fields=False if you're inheriting from the model and intended this)" + ) + + +def extract_validators(namespace: Dict[str, Any]) -> Dict[str, List[Validator]]: + validators: Dict[str, List[Validator]] = {} + for var_name, value in namespace.items(): + validator_config = getattr(value, VALIDATOR_CONFIG_KEY, None) + if validator_config: + fields, v = validator_config + for field in fields: + if field in validators: + validators[field].append(v) + else: + validators[field] = [v] + return validators + + +def extract_root_validators(namespace: Dict[str, Any]) -> Tuple[List[AnyCallable], List[Tuple[bool, AnyCallable]]]: + from inspect import signature + + pre_validators: List[AnyCallable] = [] + post_validators: List[Tuple[bool, AnyCallable]] = [] + for name, value in namespace.items(): + validator_config: Optional[Validator] = getattr(value, ROOT_VALIDATOR_CONFIG_KEY, None) + if validator_config: + sig = signature(validator_config.func) + args = list(sig.parameters.keys()) + if args[0] == 'self': + raise ConfigError( + f'Invalid signature for root validator {name}: {sig}, "self" not permitted as first argument, ' + f'should be: (cls, values).' + ) + if len(args) != 2: + raise ConfigError(f'Invalid signature for root validator {name}: {sig}, should be: (cls, values).') + # check function signature + if validator_config.pre: + pre_validators.append(validator_config.func) + else: + post_validators.append((validator_config.skip_on_failure, validator_config.func)) + return pre_validators, post_validators + + +def inherit_validators(base_validators: 'ValidatorListDict', validators: 'ValidatorListDict') -> 'ValidatorListDict': + for field, field_validators in base_validators.items(): + if field not in validators: + validators[field] = [] + validators[field] += field_validators + return validators + + +def make_generic_validator(validator: AnyCallable) -> 'ValidatorCallable': + """ + Make a generic function which calls a validator with the right arguments. + + Unfortunately other approaches (eg. return a partial of a function that builds the arguments) is slow, + hence this laborious way of doing things. + + It's done like this so validators don't all need **kwargs in their signature, eg. any combination of + the arguments "values", "fields" and/or "config" are permitted. + """ + from inspect import signature + + sig = signature(validator) + args = list(sig.parameters.keys()) + first_arg = args.pop(0) + if first_arg == 'self': + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, "self" not permitted as first argument, ' + f'should be: (cls, value, values, config, field), "values", "config" and "field" are all optional.' + ) + elif first_arg == 'cls': + # assume the second argument is value + return wraps(validator)(_generic_validator_cls(validator, sig, set(args[1:]))) + else: + # assume the first argument was value which has already been removed + return wraps(validator)(_generic_validator_basic(validator, sig, set(args))) + + +def prep_validators(v_funcs: Iterable[AnyCallable]) -> 'ValidatorsList': + return [make_generic_validator(f) for f in v_funcs if f] + + +all_kwargs = {'values', 'field', 'config'} + + +def _generic_validator_cls(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable': + # assume the first argument is value + has_kwargs = False + if 'kwargs' in args: + has_kwargs = True + args -= {'kwargs'} + + if not args.issubset(all_kwargs): + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, should be: ' + f'(cls, value, values, config, field), "values", "config" and "field" are all optional.' + ) + + if has_kwargs: + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config) + elif args == set(): + return lambda cls, v, values, field, config: validator(cls, v) + elif args == {'values'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values) + elif args == {'field'}: + return lambda cls, v, values, field, config: validator(cls, v, field=field) + elif args == {'config'}: + return lambda cls, v, values, field, config: validator(cls, v, config=config) + elif args == {'values', 'field'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field) + elif args == {'values', 'config'}: + return lambda cls, v, values, field, config: validator(cls, v, values=values, config=config) + elif args == {'field', 'config'}: + return lambda cls, v, values, field, config: validator(cls, v, field=field, config=config) + else: + # args == {'values', 'field', 'config'} + return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config) + + +def _generic_validator_basic(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable': + has_kwargs = False + if 'kwargs' in args: + has_kwargs = True + args -= {'kwargs'} + + if not args.issubset(all_kwargs): + raise ConfigError( + f'Invalid signature for validator {validator}: {sig}, should be: ' + f'(value, values, config, field), "values", "config" and "field" are all optional.' + ) + + if has_kwargs: + return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config) + elif args == set(): + return lambda cls, v, values, field, config: validator(v) + elif args == {'values'}: + return lambda cls, v, values, field, config: validator(v, values=values) + elif args == {'field'}: + return lambda cls, v, values, field, config: validator(v, field=field) + elif args == {'config'}: + return lambda cls, v, values, field, config: validator(v, config=config) + elif args == {'values', 'field'}: + return lambda cls, v, values, field, config: validator(v, values=values, field=field) + elif args == {'values', 'config'}: + return lambda cls, v, values, field, config: validator(v, values=values, config=config) + elif args == {'field', 'config'}: + return lambda cls, v, values, field, config: validator(v, field=field, config=config) + else: + # args == {'values', 'field', 'config'} + return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config) + + +def gather_all_validators(type_: 'ModelOrDc') -> Dict[str, 'AnyClassMethod']: + all_attributes = ChainMap(*[cls.__dict__ for cls in type_.__mro__]) # type: ignore[arg-type,var-annotated] + return { + k: v + for k, v in all_attributes.items() + if hasattr(v, VALIDATOR_CONFIG_KEY) or hasattr(v, ROOT_VALIDATOR_CONFIG_KEY) + } diff --git a/libs/win/pydantic/color.cp37-win_amd64.pyd b/libs/win/pydantic/color.cp37-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..eaea390c89d1594b20a378f01a1b80700f24b214 GIT binary patch literal 224256 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