Browse Source

图片解析应用

master
maojian 6 months ago
commit
98388440de
  1. 11
      .idea/CnOCRService.iml
  2. 4
      .idea/encodings.xml
  3. 10
      .idea/misc.xml
  4. 8
      .idea/modules.xml
  5. 6
      .idea/vcs.xml
  6. 536
      .idea/workspace.xml
  7. 19
      CnOcr.py
  8. 1
      Lib/site-packages/Flask-2.2.5.dist-info/INSTALLER
  9. 28
      Lib/site-packages/Flask-2.2.5.dist-info/LICENSE.rst
  10. 123
      Lib/site-packages/Flask-2.2.5.dist-info/METADATA
  11. 53
      Lib/site-packages/Flask-2.2.5.dist-info/RECORD
  12. 5
      Lib/site-packages/Flask-2.2.5.dist-info/WHEEL
  13. 2
      Lib/site-packages/Flask-2.2.5.dist-info/entry_points.txt
  14. 1
      Lib/site-packages/Flask-2.2.5.dist-info/top_level.txt
  15. 1
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/INSTALLER
  16. 7
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/LICENSE
  17. 148
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/METADATA
  18. 16
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/RECORD
  19. 6
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/WHEEL
  20. 1
      Lib/site-packages/Flask_Cors-5.0.0.dist-info/top_level.txt
  21. 132
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/AUTHORS.md
  22. 1
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/INSTALLER
  23. 25
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/LICENSE
  24. 29
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/METADATA
  25. 27
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/RECORD
  26. 6
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/WHEEL
  27. 1
      Lib/site-packages/Flask_RESTful-0.3.10.dist-info/top_level.txt
  28. 1
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER
  29. 28
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst
  30. 93
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/METADATA
  31. 14
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/RECORD
  32. 5
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL
  33. 1
      Lib/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt
  34. 1
      Lib/site-packages/PyYAML-6.0.1.dist-info/INSTALLER
  35. 20
      Lib/site-packages/PyYAML-6.0.1.dist-info/LICENSE
  36. 46
      Lib/site-packages/PyYAML-6.0.1.dist-info/METADATA
  37. 43
      Lib/site-packages/PyYAML-6.0.1.dist-info/RECORD
  38. 5
      Lib/site-packages/PyYAML-6.0.1.dist-info/WHEEL
  39. 2
      Lib/site-packages/PyYAML-6.0.1.dist-info/top_level.txt
  40. 1
      Lib/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER
  41. 28
      Lib/site-packages/Werkzeug-2.2.3.dist-info/LICENSE.rst
  42. 126
      Lib/site-packages/Werkzeug-2.2.3.dist-info/METADATA
  43. 98
      Lib/site-packages/Werkzeug-2.2.3.dist-info/RECORD
  44. 5
      Lib/site-packages/Werkzeug-2.2.3.dist-info/WHEEL
  45. 1
      Lib/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt
  46. BIN
      Lib/site-packages/__pycache__/isympy.cpython-37.pyc
  47. BIN
      Lib/site-packages/__pycache__/readline.cpython-37.pyc
  48. BIN
      Lib/site-packages/__pycache__/six.cpython-37.pyc
  49. BIN
      Lib/site-packages/__pycache__/typing_extensions.cpython-37.pyc
  50. 33
      Lib/site-packages/_yaml/__init__.py
  51. BIN
      Lib/site-packages/_yaml/__pycache__/__init__.cpython-37.pyc
  52. 1
      Lib/site-packages/aniso8601-9.0.1.dist-info/INSTALLER
  53. 27
      Lib/site-packages/aniso8601-9.0.1.dist-info/LICENSE
  54. 513
      Lib/site-packages/aniso8601-9.0.1.dist-info/METADATA
  55. 60
      Lib/site-packages/aniso8601-9.0.1.dist-info/RECORD
  56. 6
      Lib/site-packages/aniso8601-9.0.1.dist-info/WHEEL
  57. 1
      Lib/site-packages/aniso8601-9.0.1.dist-info/top_level.txt
  58. 26
      Lib/site-packages/aniso8601/__init__.py
  59. BIN
      Lib/site-packages/aniso8601/__pycache__/__init__.cpython-37.pyc
  60. BIN
      Lib/site-packages/aniso8601/__pycache__/compat.cpython-37.pyc
  61. BIN
      Lib/site-packages/aniso8601/__pycache__/date.cpython-37.pyc
  62. BIN
      Lib/site-packages/aniso8601/__pycache__/decimalfraction.cpython-37.pyc
  63. BIN
      Lib/site-packages/aniso8601/__pycache__/duration.cpython-37.pyc
  64. BIN
      Lib/site-packages/aniso8601/__pycache__/exceptions.cpython-37.pyc
  65. BIN
      Lib/site-packages/aniso8601/__pycache__/interval.cpython-37.pyc
  66. BIN
      Lib/site-packages/aniso8601/__pycache__/resolution.cpython-37.pyc
  67. BIN
      Lib/site-packages/aniso8601/__pycache__/time.cpython-37.pyc
  68. BIN
      Lib/site-packages/aniso8601/__pycache__/timezone.cpython-37.pyc
  69. BIN
      Lib/site-packages/aniso8601/__pycache__/utcoffset.cpython-37.pyc
  70. 614
      Lib/site-packages/aniso8601/builders/__init__.py
  71. BIN
      Lib/site-packages/aniso8601/builders/__pycache__/__init__.cpython-37.pyc
  72. BIN
      Lib/site-packages/aniso8601/builders/__pycache__/python.cpython-37.pyc
  73. 705
      Lib/site-packages/aniso8601/builders/python.py
  74. 7
      Lib/site-packages/aniso8601/builders/tests/__init__.py
  75. BIN
      Lib/site-packages/aniso8601/builders/tests/__pycache__/__init__.cpython-37.pyc
  76. BIN
      Lib/site-packages/aniso8601/builders/tests/__pycache__/test_init.cpython-37.pyc
  77. BIN
      Lib/site-packages/aniso8601/builders/tests/__pycache__/test_python.cpython-37.pyc
  78. 838
      Lib/site-packages/aniso8601/builders/tests/test_init.py
  79. 1710
      Lib/site-packages/aniso8601/builders/tests/test_python.py
  80. 24
      Lib/site-packages/aniso8601/compat.py
  81. 161
      Lib/site-packages/aniso8601/date.py
  82. 12
      Lib/site-packages/aniso8601/decimalfraction.py
  83. 291
      Lib/site-packages/aniso8601/duration.py
  84. 51
      Lib/site-packages/aniso8601/exceptions.py
  85. 350
      Lib/site-packages/aniso8601/interval.py
  86. 27
      Lib/site-packages/aniso8601/resolution.py
  87. 7
      Lib/site-packages/aniso8601/tests/__init__.py
  88. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/__init__.cpython-37.pyc
  89. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/compat.cpython-37.pyc
  90. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_compat.cpython-37.pyc
  91. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_date.cpython-37.pyc
  92. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_decimalfraction.cpython-37.pyc
  93. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_duration.cpython-37.pyc
  94. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_init.cpython-37.pyc
  95. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_interval.cpython-37.pyc
  96. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_time.cpython-37.pyc
  97. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_timezone.cpython-37.pyc
  98. BIN
      Lib/site-packages/aniso8601/tests/__pycache__/test_utcoffset.cpython-37.pyc
  99. 16
      Lib/site-packages/aniso8601/tests/compat.py
  100. 27
      Lib/site-packages/aniso8601/tests/test_compat.py

11
.idea/CnOCRService.iml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

4
.idea/encodings.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

10
.idea/misc.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>
</project>

8
.idea/modules.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/CnOCRService.iml" filepath="$PROJECT_DIR$/.idea/CnOCRService.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

536
.idea/workspace.xml

@ -0,0 +1,536 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="62fb0e8b-1fdf-4b91-9565-798bb9e19152" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="CoverageDataManager">
<SUITE FILE_PATH="coverage/CnOCRService$zk_util.coverage" NAME="zk_util Coverage Results" MODIFIED="1728975622411" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/CnOCRService$load_scenes.coverage" NAME="load_scenes Coverage Results" MODIFIED="1728902059389" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/CnOCRService$zk_listener.coverage" NAME="zk_listener Coverage Results" MODIFIED="1728898778709" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/CnOCRService$dataUtil.coverage" NAME="dataUtil Coverage Results" MODIFIED="1726123342941" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/CnOCRService$app.coverage" NAME="app Coverage Results" MODIFIED="1726050413312" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/CnOcr.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="342">
<caret line="18" column="5" lean-forward="true" selection-start-line="18" selection-start-column="5" selection-end-line="18" selection-end-column="5" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/ocr_handler.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="57">
<caret line="3" column="10" selection-start-line="3" selection-start-column="10" selection-end-line="3" selection-end-column="10" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>prod</find>
<find>DataWatch</find>
<find>1931</find>
<find>Successfully wrote queue</find>
<find>Received OCR task</find>
<find>stop_event</find>
<find>Error writing queue item to file</find>
<find>cloce</find>
<find>open</find>
<find>kafka</find>
<find>VOCAB_FP</find>
</findStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/dataUtil.py" />
<option value="$PROJECT_DIR$/config_loader.py" />
<option value="$PROJECT_DIR$/config.yml" />
<option value="$PROJECT_DIR$/zk_listener.py" />
<option value="$PROJECT_DIR$/load_scenes.py" />
<option value="$PROJECT_DIR$/global_dict.py" />
<option value="$PROJECT_DIR$/zk_util.py" />
<option value="$PROJECT_DIR$/app.py" />
<option value="$PROJECT_DIR$/queue_manager.py" />
<option value="$PROJECT_DIR$/CnOcr.py" />
<option value="$PROJECT_DIR$/ocr_handler.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="-12" />
<option name="y" value="-12" />
<option name="width" value="1944" />
<option name="height" value="1044" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="CnOCRService" type="b2602c69:ProjectViewProjectNode" />
<item name="CnOCRService" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\毛健\PycharmProjects\CnOCRService" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Python.app">
<configuration name="app" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="CnOCRService" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="dataUtil" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="CnOCRService" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/dataUtil.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="load_scenes" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="CnOCRService" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/load_scenes.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="zk_listener" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="CnOCRService" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="C:\Users\毛健\PycharmProjects\CnOCRService\zk_util.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="zk_util" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="CnOCRService" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/zk_util.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.app" />
<item itemvalue="Python.zk_util" />
<item itemvalue="Python.load_scenes" />
<item itemvalue="Python.zk_listener" />
<item itemvalue="Python.dataUtil" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="62fb0e8b-1fdf-4b91-9565-798bb9e19152" name="Default Changelist" comment="" />
<created>1726036962320</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1726036962320</updated>
<workItem from="1726036966579" duration="22732000" />
<workItem from="1726738964285" duration="662000" />
<workItem from="1728701735578" duration="6034000" />
<workItem from="1728872678803" duration="31923000" />
<workItem from="1730278275853" duration="234000" />
<workItem from="1730690391160" duration="45000" />
<workItem from="1730692474286" duration="627000" />
<workItem from="1730946197214" duration="2750000" />
<workItem from="1731373999568" duration="2486000" />
<workItem from="1731575462691" duration="6511000" />
<workItem from="1731667531083" duration="18000" />
<workItem from="1732499598045" duration="2391000" />
<workItem from="1733278436428" duration="43000" />
<workItem from="1735283750296" duration="646000" />
</task>
<task id="LOCAL-00001" summary="新增zk监控,版本控制">
<created>1728958677000</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1728958677000</updated>
</task>
<task id="LOCAL-00002" summary="新增zk监控,版本控制">
<created>1728977076914</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1728977076914</updated>
</task>
<task id="LOCAL-00003" summary="新增zk监控,版本控制">
<created>1728977089049</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1728977089049</updated>
</task>
<task id="LOCAL-00004" summary="新增zk监控,版本控制">
<created>1728978278870</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1728978278870</updated>
</task>
<task id="LOCAL-00005" summary="新增zk监控,版本控制">
<created>1728982994791</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1728982994791</updated>
</task>
<task id="LOCAL-00006" summary="新增zk监控,版本控制">
<created>1728984597368</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1728984597368</updated>
</task>
<task id="LOCAL-00007" summary="新增调式模型逻辑">
<created>1731579772217</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1731579772217</updated>
</task>
<task id="LOCAL-00008" summary="新增调式模型逻辑">
<created>1731580317498</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1731580317498</updated>
</task>
<option name="localTasksCounter" value="9" />
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="77102000" />
</component>
<component name="TodoView" selected-index="4">
<todo-panel id="selected-file">
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="-6" y="-6" width="1453" height="865" extended-state="6" />
<layout>
<window_info id="Favorites" order="0" side_tool="true" />
<window_info content_ui="combo" id="Project" order="1" visible="true" weight="0.14467184" />
<window_info id="Structure" order="2" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Database Changes" order="0" />
<window_info active="true" anchor="bottom" id="Terminal" order="1" sideWeight="0.4982357" visible="true" weight="0.32833788" />
<window_info anchor="bottom" id="Run" order="2" sideWeight="0.5017643" side_tool="true" weight="0.32833788" />
<window_info anchor="bottom" id="Event Log" order="3" sideWeight="0.50039744" side_tool="true" weight="0.3299532" />
<window_info anchor="bottom" id="Version Control" order="4" weight="0.3299532" />
<window_info anchor="bottom" id="Python Console" order="5" weight="0.32917318" />
<window_info anchor="bottom" id="Docker" order="6" show_stripe_button="false" />
<window_info anchor="bottom" id="Message" order="7" />
<window_info anchor="bottom" id="Find" order="8" />
<window_info anchor="bottom" id="Debug" order="9" sideWeight="0.49960256" weight="0.39937598" />
<window_info anchor="bottom" id="Cvs" order="10" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="11" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="12" sideWeight="0.49960256" weight="0.3299532" />
<window_info anchor="right" id="Database" order="0" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="1" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="SciView" order="2" />
<window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
</layout>
<layout-to-restore>
<window_info id="Favorites" order="0" side_tool="true" />
<window_info content_ui="combo" id="Project" order="1" visible="true" weight="0.13434023" />
<window_info id="Structure" order="2" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Database Changes" order="0" />
<window_info anchor="bottom" id="Terminal" order="1" sideWeight="0.49960256" weight="0.3299532" />
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.50039744" side_tool="true" visible="true" weight="0.39937598" />
<window_info anchor="bottom" id="Event Log" order="3" side_tool="true" weight="0.3299532" />
<window_info anchor="bottom" id="Version Control" order="4" weight="0.3299532" />
<window_info anchor="bottom" id="Python Console" order="5" weight="0.32917318" />
<window_info anchor="bottom" id="Docker" order="6" show_stripe_button="false" />
<window_info anchor="bottom" id="Message" order="7" />
<window_info anchor="bottom" id="Find" order="8" />
<window_info anchor="bottom" id="Debug" order="9" sideWeight="0.49960256" weight="0.39937598" />
<window_info anchor="bottom" id="Cvs" order="10" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="11" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="12" weight="0.3299532" />
<window_info anchor="right" id="Database" order="0" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="1" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="SciView" order="2" />
<window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
</layout-to-restore>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="Vcs.Log.History.Properties">
<option name="COLUMN_ORDER">
<list>
<option value="0" />
<option value="2" />
<option value="3" />
<option value="1" />
</list>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="新增zk监控,版本控制" />
<MESSAGE value="新增调式模型逻辑" />
<option name="LAST_COMMIT_MESSAGE" value="新增调式模型逻辑" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/Lib/site-packages/kafka/producer/kafka.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="688">
<caret line="547" column="52" lean-forward="true" selection-start-line="542" selection-start-column="30" selection-end-line="547" selection-end-column="52" />
<folding>
<element signature="e#0#38#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/AppData/Local/Programs/Python/Python37/Lib/logging/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="277">
<caret line="1929" column="4" selection-start-line="1929" selection-start-column="4" selection-end-line="1929" selection-end-column="4" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/AppData/Local/Programs/Python/Python37/Lib/queue.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="444">
<caret line="91" column="68" lean-forward="true" selection-start-line="91" selection-start-column="11" selection-end-line="91" selection-end-column="68" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config.yml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="270">
<caret line="10" selection-end-line="10" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/zk_util.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="675">
<caret line="31" column="38" selection-start-line="31" selection-start-column="38" selection-end-line="31" selection-end-column="38" />
<folding>
<element signature="e#13#49#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/load_scenes.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="189">
<caret line="9" column="19" selection-start-line="9" selection-start-column="19" selection-end-line="9" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/dataUtil.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="189">
<caret line="7" column="4" selection-start-line="7" selection-start-column="4" selection-end-line="7" selection-end-column="4" />
<folding>
<element signature="e#13#24#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/Lib/site-packages/flask_restful/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="417">
<caret line="378" column="8" selection-start-line="378" selection-start-column="8" selection-end-line="378" selection-end-column="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/queue_manager.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="171">
<caret line="9" column="12" selection-start-line="9" selection-start-column="12" selection-end-line="9" selection-end-column="12" />
<folding>
<element signature="e#13#42#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/global_dict.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="19">
<caret line="1" selection-start-line="1" selection-end-line="1" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/app.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="266">
<caret line="101" column="76" selection-start-line="101" selection-start-column="76" selection-end-line="101" selection-end-column="76" />
<folding>
<element signature="e#14#27#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/config_loader.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="114">
<caret line="6" column="48" selection-start-line="6" selection-start-column="48" selection-end-line="6" selection-end-column="48" />
<folding>
<element signature="e#13#24#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/CnOcr.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="342">
<caret line="18" column="5" lean-forward="true" selection-start-line="18" selection-start-column="5" selection-end-line="18" selection-end-column="5" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/ocr_handler.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="57">
<caret line="3" column="10" selection-start-line="3" selection-start-column="10" selection-end-line="3" selection-end-column="10" />
</state>
</provider>
</entry>
</component>
</project>

19
CnOcr.py

@ -0,0 +1,19 @@
class CnOcr(object):
def __init__(
self,
rec_model_name: str = 'densenet_lite_136-fc',
*,
det_model_name: str = 'ch_PP-OCRv3_det',
cand_alphabet: Optional[Union[Collection, str]] = None,
context: str = 'cpu', # ['cpu', 'gpu', 'cuda']
rec_model_fp: Optional[str] = None,
rec_model_backend: str = 'onnx', # ['pytorch', 'onnx']
rec_vocab_fp: Union[str, Path] = VOCAB_FP,
rec_more_configs: Optional[Dict[str, Any]] = None,
rec_root: Union[str, Path] = data_dir(),
det_model_fp: Optional[str] = None,
det_model_backend: str = 'onnx', # ['pytorch', 'onnx']
det_more_configs: Optional[Dict[str, Any]] = None,
det_root: Union[str, Path] = det_data_dir(),
**kwargs,
)

1
Lib/site-packages/Flask-2.2.5.dist-info/INSTALLER

@ -0,0 +1 @@
pip

28
Lib/site-packages/Flask-2.2.5.dist-info/LICENSE.rst

@ -0,0 +1,28 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

123
Lib/site-packages/Flask-2.2.5.dist-info/METADATA

@ -0,0 +1,123 @@
Metadata-Version: 2.1
Name: Flask
Version: 2.2.5
Summary: A simple framework for building complex web applications.
Home-page: https://palletsprojects.com/p/flask
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://flask.palletsprojects.com/
Project-URL: Changes, https://flask.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/flask/
Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
Requires-Dist: Werkzeug (>=2.2.2)
Requires-Dist: Jinja2 (>=3.0)
Requires-Dist: itsdangerous (>=2.0)
Requires-Dist: click (>=8.0)
Requires-Dist: importlib-metadata (>=3.6.0) ; python_version < "3.10"
Provides-Extra: async
Requires-Dist: asgiref (>=3.2) ; extra == 'async'
Provides-Extra: dotenv
Requires-Dist: python-dotenv ; extra == 'dotenv'
Flask
=====
Flask is a lightweight `WSGI`_ web application framework. It is designed
to make getting started quick and easy, with the ability to scale up to
complex applications. It began as a simple wrapper around `Werkzeug`_
and `Jinja`_ and has become one of the most popular Python web
application frameworks.
Flask offers suggestions, but doesn't enforce any dependencies or
project layout. It is up to the developer to choose the tools and
libraries they want to use. There are many extensions provided by the
community that make adding new functionality easy.
.. _WSGI: https://wsgi.readthedocs.io/
.. _Werkzeug: https://werkzeug.palletsprojects.com/
.. _Jinja: https://jinja.palletsprojects.com/
Installing
----------
Install and update using `pip`_:
.. code-block:: text
$ pip install -U Flask
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
# save this as app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
.. code-block:: text
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Contributing
------------
For guidance on setting up a development environment and how to make a
contribution to Flask, see the `contributing guidelines`_.
.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst
Donate
------
The Pallets organization develops and supports Flask and the libraries
it uses. In order to grow the community of contributors and users, and
allow the maintainers to devote more time to the projects, `please
donate today`_.
.. _please donate today: https://palletsprojects.com/donate
Links
-----
- Documentation: https://flask.palletsprojects.com/
- Changes: https://flask.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Flask/
- Source Code: https://github.com/pallets/flask/
- Issue Tracker: https://github.com/pallets/flask/issues/
- Website: https://palletsprojects.com/p/flask/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

53
Lib/site-packages/Flask-2.2.5.dist-info/RECORD

@ -0,0 +1,53 @@
flask/__init__.py,sha256=GJgAILDWhW_DQljuoJ4pk9zBUy70zPPu-VZ6kLyiVI4,2890
flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
flask/app.py,sha256=ue4tEeDnr3m-eSEwz7OJ1_wafSYl3fl6eo-NLFgNNJQ,99141
flask/blueprints.py,sha256=fenhKP_Sh5eU6qtWeHacg1GVeun4pQzK2vq8sNDd1hY,27266
flask/cli.py,sha256=pLmnWObe_G4_ZAFQdh7kgwqPMxRXm4oUhaUSBpJMeq4,33532
flask/config.py,sha256=Ubo_juzSYsAKqD2vD3vm6mjsPo3EOJDdSEzYq8lKTJI,12585
flask/ctx.py,sha256=bGEQQuF2_cHqZ3ZNMeMeEG8HOLJkDlL88u2BBxCrRao,14829
flask/debughelpers.py,sha256=_RvAL3TW5lqMJeCVWtTU6rSDJC7jnRaBL6OEkVmooyU,5511
flask/globals.py,sha256=EX0XdX73BTWdVF0UHDSNet2ER3kI6sKveo3_o5IOs98,3187
flask/helpers.py,sha256=XTHRgLlyxeEzR988q63-4OY8RswTscR-5exFxK10CLU,25280
flask/logging.py,sha256=WYng0bLTRS_CJrocGcCLJpibHf1lygHE_pg-KoUIQ4w,2293
flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
flask/scaffold.py,sha256=EKx-Tr5BXLzeKKvq3ZAi_2oUQVZuC4OJSJTocyDXsSo,35958
flask/sessions.py,sha256=adWCRnJYETJcjjhlcvUgZR5S0DMqKQctS0nzkY9g9Us,15927
flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136
flask/templating.py,sha256=1P4OzvSnA2fsJTYgQT3G4owVKsuOz8XddCiR6jMHGJ0,7419
flask/testing.py,sha256=JtHRQY7mIH39SM4S51svAr8e7Xk87dqMb30Z6Dyv9TA,10706
flask/typing.py,sha256=KgxegTF9v9WvuongeF8LooIvpZPauzGrq9ZXf3gBlYc,2969
flask/views.py,sha256=LulttWL4owVFlgwrJi8GCNM4inC3xbs2IBlY31bdCS4,6765
flask/wrappers.py,sha256=el3tn1LgSUV0eNGgYMjKICT5I7qGJgbpIhvci4nrwQ8,5702
flask/json/__init__.py,sha256=TOwldHT3_kFaXHlORKi9yCWt7dbPNB0ovdHHQWlSRzY,11175
flask/json/provider.py,sha256=jXCNypf11PN4ngQjEt6LnSdCWQ1yHIAkNLHlXQlCB-A,10674
flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857
Flask-2.2.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
Flask-2.2.5.dist-info/METADATA,sha256=rZTjr5v4M7HB-zC-w2Y0ZU96OYSGBb-Hm15jlLJhs3g,3889
Flask-2.2.5.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
Flask-2.2.5.dist-info/entry_points.txt,sha256=s3MqQpduU25y4dq3ftBYD6bMVdVnbMpZP-sUNw0zw0k,41
Flask-2.2.5.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6
Flask-2.2.5.dist-info/RECORD,,
../../Scripts/flask.exe,sha256=CT8LhI-frVdxvcOzhTdJW6AhmXXe_S6MOnhXlUfMyR8,102780
Flask-2.2.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
flask/json/__pycache__/provider.cpython-37.pyc,,
flask/json/__pycache__/tag.cpython-37.pyc,,
flask/json/__pycache__/__init__.cpython-37.pyc,,
flask/__pycache__/app.cpython-37.pyc,,
flask/__pycache__/blueprints.cpython-37.pyc,,
flask/__pycache__/cli.cpython-37.pyc,,
flask/__pycache__/config.cpython-37.pyc,,
flask/__pycache__/ctx.cpython-37.pyc,,
flask/__pycache__/debughelpers.cpython-37.pyc,,
flask/__pycache__/globals.cpython-37.pyc,,
flask/__pycache__/helpers.cpython-37.pyc,,
flask/__pycache__/logging.cpython-37.pyc,,
flask/__pycache__/scaffold.cpython-37.pyc,,
flask/__pycache__/sessions.cpython-37.pyc,,
flask/__pycache__/signals.cpython-37.pyc,,
flask/__pycache__/templating.cpython-37.pyc,,
flask/__pycache__/testing.cpython-37.pyc,,
flask/__pycache__/typing.cpython-37.pyc,,
flask/__pycache__/views.cpython-37.pyc,,
flask/__pycache__/wrappers.cpython-37.pyc,,
flask/__pycache__/__init__.cpython-37.pyc,,
flask/__pycache__/__main__.cpython-37.pyc,,

5
Lib/site-packages/Flask-2.2.5.dist-info/WHEEL

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.40.0)
Root-Is-Purelib: true
Tag: py3-none-any

2
Lib/site-packages/Flask-2.2.5.dist-info/entry_points.txt

@ -0,0 +1,2 @@
[console_scripts]
flask = flask.cli:main

1
Lib/site-packages/Flask-2.2.5.dist-info/top_level.txt

@ -0,0 +1 @@
flask

1
Lib/site-packages/Flask_Cors-5.0.0.dist-info/INSTALLER

@ -0,0 +1 @@
pip

7
Lib/site-packages/Flask_Cors-5.0.0.dist-info/LICENSE

@ -0,0 +1,7 @@
Copyright (C) 2016 Cory Dolphin, Olin College
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.

148
Lib/site-packages/Flask_Cors-5.0.0.dist-info/METADATA

@ -0,0 +1,148 @@
Metadata-Version: 2.1
Name: Flask-Cors
Version: 5.0.0
Summary: A Flask extension adding a decorator for CORS support
Home-page: https://github.com/corydolphin/flask-cors
Author: Cory Dolphin
Author-email: corydolphin@gmail.com
License: MIT
Platform: any
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
License-File: LICENSE
Requires-Dist: Flask >=0.9
Flask-CORS
==========
|Build Status| |Latest Version| |Supported Python versions|
|License|
A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible.
This package has a simple philosophy: when you want to enable CORS, you wish to enable it for all use cases on a domain.
This means no mucking around with different allowed headers, methods, etc.
By default, submission of cookies across domains is disabled due to the security implications.
Please see the documentation for how to enable credential'ed requests, and please make sure you add some sort of `CSRF <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`__ protection before doing so!
Installation
------------
Install the extension with using pip, or easy\_install.
.. code:: bash
$ pip install -U flask-cors
Usage
-----
This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods.
It allows parameterization of all CORS headers on a per-resource level.
The package also contains a decorator, for those who prefer this approach.
Simple Usage
~~~~~~~~~~~~
In the simplest case, initialize the Flask-Cors extension with default arguments in order to allow CORS for all domains on all routes.
See the full list of options in the `documentation <https://flask-cors.corydolphin.com/en/latest/api.html#extension>`__.
.. code:: python
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
Resource specific CORS
^^^^^^^^^^^^^^^^^^^^^^
Alternatively, you can specify CORS options on a resource and origin level of granularity by passing a dictionary as the `resources` option, mapping paths to a set of options.
See the full list of options in the `documentation <https://flask-cors.corydolphin.com/en/latest/api.html#extension>`__.
.. code:: python
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
@app.route("/api/v1/users")
def list_users():
return "user example"
Route specific CORS via decorator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This extension also exposes a simple decorator to decorate flask routes with.
Simply add ``@cross_origin()`` below a call to Flask's ``@app.route(..)`` to allow CORS on a given route.
See the full list of options in the `decorator documentation <https://flask-cors.corydolphin.com/en/latest/api.html#decorator>`__.
.. code:: python
@app.route("/")
@cross_origin()
def helloWorld():
return "Hello, cross-origin-world!"
Documentation
-------------
For a full list of options, please see the full `documentation <https://flask-cors.corydolphin.com/en/latest/api.html>`__
Troubleshooting
---------------
If things aren't working as you expect, enable logging to help understand what is going on under the hood, and why.
.. code:: python
logging.getLogger('flask_cors').level = logging.DEBUG
Tests
-----
A simple set of tests is included in ``test/``.
To run, install nose, and simply invoke ``nosetests`` or ``python setup.py test`` to exercise the tests.
If nosetests does not work for you, due to it no longer working with newer python versions.
You can use pytest to run the tests instead.
Contributing
------------
Questions, comments or improvements?
Please create an issue on `Github <https://github.com/corydolphin/flask-cors>`__, tweet at `@corydolphin <https://twitter.com/corydolphin>`__ or send me an email.
I do my best to include every contribution proposed in any way that I can.
Credits
-------
This Flask extension is based upon the `Decorator for the HTTP Access Control <https://web.archive.org/web/20190128010149/http://flask.pocoo.org/snippets/56/>`__ written by Armin Ronacher.
.. |Build Status| image:: https://github.com/corydolphin/flask-cors/actions/workflows/unittests.yaml/badge.svg
:target: https://travis-ci.org/corydolphin/flask-cors
.. |Latest Version| image:: https://img.shields.io/pypi/v/Flask-Cors.svg
:target: https://pypi.python.org/pypi/Flask-Cors/
.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/Flask-Cors.svg
:target: https://img.shields.io/pypi/pyversions/Flask-Cors.svg
.. |License| image:: http://img.shields.io/:license-mit-blue.svg
:target: https://pypi.python.org/pypi/Flask-Cors/

16
Lib/site-packages/Flask_Cors-5.0.0.dist-info/RECORD

@ -0,0 +1,16 @@
flask_cors/__init__.py,sha256=wZDCvPTHspA2g1VV7KyKN7R-uCdBnirTlsCzgPDcQtI,792
flask_cors/core.py,sha256=y76xxLasWTdV_3ka19IxpdJPOgROBZQZ5L8t20IjqRA,14252
flask_cors/decorator.py,sha256=BeJsyX1wYhVKWN04FAhb6z8YqffiRr7wKqwzHPap4bw,5009
flask_cors/extension.py,sha256=gzv6zWUwSDYlGHBWzMuTI_hoQ7gQmp9DlcAcrKTVHdw,8602
flask_cors/version.py,sha256=JzYPYpvaglqIJRGCDrh5-hYmXI0ISrDDed0V1QQZAGU,22
Flask_Cors-5.0.0.dist-info/LICENSE,sha256=bhob3FSDTB4HQMvOXV9vLK4chG_Sp_SCsRZJWU-vvV0,1069
Flask_Cors-5.0.0.dist-info/METADATA,sha256=V2L_s849dFlZXsOhcgXVqv5Slj_JKSVuiiuRgDOft5s,5474
Flask_Cors-5.0.0.dist-info/WHEEL,sha256=WDDPHYzpiOIm6GP1C2_8y8W6q16ICddAgOHlhTje9Qc,109
Flask_Cors-5.0.0.dist-info/top_level.txt,sha256=aWye_0QNZPp_QtPF4ZluLHqnyVLT9CPJsfiGhwqkWuo,11
Flask_Cors-5.0.0.dist-info/RECORD,,
Flask_Cors-5.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
flask_cors/__pycache__/core.cpython-37.pyc,,
flask_cors/__pycache__/decorator.cpython-37.pyc,,
flask_cors/__pycache__/extension.cpython-37.pyc,,
flask_cors/__pycache__/version.cpython-37.pyc,,
flask_cors/__pycache__/__init__.cpython-37.pyc,,

6
Lib/site-packages/Flask_Cors-5.0.0.dist-info/WHEEL

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: setuptools (74.0.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

1
Lib/site-packages/Flask_Cors-5.0.0.dist-info/top_level.txt

@ -0,0 +1 @@
flask_cors

132
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/AUTHORS.md

@ -0,0 +1,132 @@
Authors
=======
A huge thanks to all of our contributors:
- Adam Chainz
- Alec Nikolas Reiter
- Alex Gaynor
- Alex M
- Alex Morken
- Andrew Dunham
- Andriy Yurchuk
- Anil Kulkarni
- Antonio Dourado
- Antonio Herraiz
- Ares Ou
- Artur Rodrigues
- Axel Haustant
- Belousow Makc
- Benjamin Dopplinger
- Bennett, Bryan
- Bohan Zhang
- Bryan Bennett
- Bulat Bochkariov
- Cameron Brandon White
- Catherine Devlin
- Dan Quirk
- Daniele Esposti
- Dario Bertini
- David Arnold
- David Baumgold
- David Boucha
- David Crawford
- Dimitris Theodorou
- Doug Black
- Evan Dale Aromin
- Eyal Levin
- Francesco Della Vedova
- Frank Stratton
- Garret Raziel
- Gary Belvin
- Gilles Dartiguelongue
- Giorgio Salluzzo
- Guillaume BINET
- Heston Liebowitz
- Hu WQ
- Jacob Magnusson
- James Booth
- James Ogura
- James Turk
- Jeff Widman
- Joakim Ekberg
- Johannes
- Jordan Yelloz
- Josh Friend
- Joshua C. Randall
- Joshua Randall
- José Fernández Ramos
- Juan Rossi
- JuneHyeon Bae
- Kamil Gałuszka
- Kevin Burke
- Kevin Deldycke
- Kevin Funk
- Kyle Conroy
- Lance Ingle
- Lars Holm Nielsen
- Luiz Armesto
- Malthe Borch
- Marek Hlobil
- Matt Wright
- Max Mautner
- Max Peterson
- Maxim
- Michael Hwang
- Michael Newman
- Miguel Grinberg
- Mihai Tomescu
- Neil Halelamien
- Nicolas Harraudeau
- Pavel Tyslyatsky
- Petrus J.v.Rensburg
- Philippe Ndiaye
- Piotr Husiatyński
- Prasanna Swaminathan
- Robert Warner
- Rod Cloutier
- Ryan Horn
- Rémi Alvergnat
- Sam Kimbrel
- Samarth Shah
- Sami Jaktholm
- Sander Sink
- Sasha Baranov
- Saul Diez-Guerra
- Sergey Romanov
- Shreyans Sheth
- Steven Leggett
- Sven-Hendrik Haase
- Usman Ehtesham Gul
- Victor Neo
- Vlad Frolov
- Vladimir Pal
- WooParadog
- Yaniv Aknin
- akash
- bret barker
- hachichaud
- jbouzekri
- jobou
- johnrichter
- justanr
- k-funk
- kelvinhammond
- kenjones
- kieran gorman
- kumy
- lyschoening
- mailto1587
- mniebla
- mozillazg
- muchosalsa
- nachinius
- nixdata
- papaeye
- pingz
- saml
- siavashg
- silasray
- soasme
- ueg1990
- y-p

1
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/INSTALLER

@ -0,0 +1 @@
pip

25
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/LICENSE

@ -0,0 +1,25 @@
Copyright (c) 2013, Twilio, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Twilio, Inc. nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

29
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/METADATA

@ -0,0 +1,29 @@
Metadata-Version: 2.1
Name: Flask-RESTful
Version: 0.3.10
Summary: Simple framework for creating REST APIs
Home-page: https://www.github.com/flask-restful/flask-restful/
Author: Twilio API Team
Author-email: help@twilio.com
License: BSD
Project-URL: Source, https://github.com/flask-restful/flask-restful
Platform: any
Classifier: Framework :: Flask
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: License :: OSI Approved :: BSD License
License-File: LICENSE
License-File: AUTHORS.md
Requires-Dist: aniso8601 (>=0.82)
Requires-Dist: Flask (>=0.8)
Requires-Dist: six (>=1.3.0)
Requires-Dist: pytz
Provides-Extra: docs
Requires-Dist: sphinx ; extra == 'docs'

27
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/RECORD

@ -0,0 +1,27 @@
flask_restful/__init__.py,sha256=KDyCbekXcfGMyV6E7neY6ZJ8b8GdM6eLtJbtRmn_nL8,28624
flask_restful/__version__.py,sha256=JbZfv76t9J7HHmoA2wdjKemYHpQE0jhBfMJIil6HEsg,46
flask_restful/fields.py,sha256=43GbFejZ3kiOb20A1QuzLXjevfsxMZSbmpOpGtW56vo,13018
flask_restful/inputs.py,sha256=561w8fjLqBq4I_7yXPHJM567ijWhpuf8d8uZnKzTehA,9118
flask_restful/reqparse.py,sha256=-xZmkyrvDFfGvFFokTtXe4J-2PWnNX4EfKolhkT995E,14681
flask_restful/representations/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
flask_restful/representations/json.py,sha256=swKwnbt7v2ioHfHkqhqbzIu_yrcP0ComlSl49IGFJOo,873
flask_restful/utils/__init__.py,sha256=jgedvOLGeTk4Sqox4WHE_vAFLP0T_PrLHO4PXaqFqxw,723
flask_restful/utils/cors.py,sha256=cZiqaHhIn0w66spRoSIdC-jIn4X_b6OlVms5eGF4Ess,2084
flask_restful/utils/crypto.py,sha256=q3PBvAYMJYybbqqQlKNF_Pqeyo9h3x5jFJuVqtEA5bA,996
Flask_RESTful-0.3.10.dist-info/AUTHORS.md,sha256=HBq00z_VgMI2xfwfUobrU16_qamdouMkpNxbR0BzaVg,1992
Flask_RESTful-0.3.10.dist-info/LICENSE,sha256=PFjoO0Jk5okmshAgMix5-RZTC0sFT_EJWpC_CtQcCyM,1485
Flask_RESTful-0.3.10.dist-info/METADATA,sha256=eTeg3NLzPPlJxKSMhedGPPQvRaQm-9lMafpxwIddLT8,1018
Flask_RESTful-0.3.10.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
Flask_RESTful-0.3.10.dist-info/top_level.txt,sha256=lNpWPlejgBAtMhCUwz_FTyJH12ul1mBZ-Uv3ZK1HiGg,14
Flask_RESTful-0.3.10.dist-info/RECORD,,
Flask_RESTful-0.3.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
flask_restful/representations/__pycache__/json.cpython-37.pyc,,
flask_restful/representations/__pycache__/__init__.cpython-37.pyc,,
flask_restful/utils/__pycache__/cors.cpython-37.pyc,,
flask_restful/utils/__pycache__/crypto.cpython-37.pyc,,
flask_restful/utils/__pycache__/__init__.cpython-37.pyc,,
flask_restful/__pycache__/fields.cpython-37.pyc,,
flask_restful/__pycache__/inputs.cpython-37.pyc,,
flask_restful/__pycache__/reqparse.cpython-37.pyc,,
flask_restful/__pycache__/__init__.cpython-37.pyc,,
flask_restful/__pycache__/__version__.cpython-37.pyc,,

6
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/WHEEL

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.40.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

1
Lib/site-packages/Flask_RESTful-0.3.10.dist-info/top_level.txt

@ -0,0 +1 @@
flask_restful

1
Lib/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER

@ -0,0 +1 @@
pip

28
Lib/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst

@ -0,0 +1,28 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

93
Lib/site-packages/MarkupSafe-2.1.5.dist-info/METADATA

@ -0,0 +1,93 @@
Metadata-Version: 2.1
Name: MarkupSafe
Version: 2.1.5
Summary: Safely add untrusted strings to HTML/XML markup.
Home-page: https://palletsprojects.com/p/markupsafe/
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/markupsafe/
Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
MarkupSafe
==========
MarkupSafe implements a text object that escapes characters so it is
safe to use in HTML and XML. Characters that have special meanings are
replaced so that they display as the actual characters. This mitigates
injection attacks, meaning untrusted user input can safely be displayed
on a page.
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U MarkupSafe
.. _pip: https://pip.pypa.io/en/stable/getting-started/
Examples
--------
.. code-block:: pycon
>>> from markupsafe import Markup, escape
>>> # escape replaces special characters and wraps in Markup
>>> escape("<script>alert(document.cookie);</script>")
Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # wrap in Markup to mark text "safe" and prevent escaping
>>> Markup("<strong>Hello</strong>")
Markup('<strong>hello</strong>')
>>> escape(Markup("<strong>Hello</strong>"))
Markup('<strong>hello</strong>')
>>> # Markup is a str subclass
>>> # methods and operators escape their arguments
>>> template = Markup("Hello <em>{name}</em>")
>>> template.format(name='"World"')
Markup('Hello <em>&#34;World&#34;</em>')
Donate
------
The Pallets organization develops and supports MarkupSafe and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
`please donate today`_.
.. _please donate today: https://palletsprojects.com/donate
Links
-----
- Documentation: https://markupsafe.palletsprojects.com/
- Changes: https://markupsafe.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/MarkupSafe/
- Source Code: https://github.com/pallets/markupsafe/
- Issue Tracker: https://github.com/pallets/markupsafe/issues/
- Chat: https://discord.gg/pallets

14
Lib/site-packages/MarkupSafe-2.1.5.dist-info/RECORD

@ -0,0 +1,14 @@
markupsafe/__init__.py,sha256=m1ysNeqf55zbEoJtaovca40ivrkEFolPlw5bGoC5Gi4,11290
markupsafe/_native.py,sha256=_Q7UsXCOvgdonCgqG3l5asANI6eo50EKnDM-mlwEC5M,1776
markupsafe/_speedups.c,sha256=n3jzzaJwXcoN8nTFyA53f3vSqsWK2vujI-v6QYifjhQ,7403
markupsafe/_speedups.cp37-win_amd64.pyd,sha256=k3EXotF4ZpaL3vGiqsU1KBjvNWTXbO3VEEaPcLEhlN0,15872
markupsafe/_speedups.pyi,sha256=f5QtwIOP0eLrxh2v5p6SmaYmlcHIGIfmz0DovaqL0OU,238
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=RjHsDbX9kKVH4zaBcmTGeYIUM4FG-KyUtKV_lu6MnsQ,1503
MarkupSafe-2.1.5.dist-info/METADATA,sha256=icNlaniV7YIQZ1BScCVqNaRtm7MAgfw8d3OBmoSVyAY,3096
MarkupSafe-2.1.5.dist-info/WHEEL,sha256=slqBGdqRnxanDn00BSYHhryEsWH_8CUurgRUvoMtK_Y,101
MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
MarkupSafe-2.1.5.dist-info/RECORD,,
MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
markupsafe/__pycache__/_native.cpython-37.pyc,,
markupsafe/__pycache__/__init__.cpython-37.pyc,,

5
Lib/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.42.0)
Root-Is-Purelib: false
Tag: cp37-cp37m-win_amd64

1
Lib/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt

@ -0,0 +1 @@
markupsafe

1
Lib/site-packages/PyYAML-6.0.1.dist-info/INSTALLER

@ -0,0 +1 @@
pip

20
Lib/site-packages/PyYAML-6.0.1.dist-info/LICENSE

@ -0,0 +1,20 @@
Copyright (c) 2017-2021 Ingy döt Net
Copyright (c) 2006-2016 Kirill Simonov
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.

46
Lib/site-packages/PyYAML-6.0.1.dist-info/METADATA

@ -0,0 +1,46 @@
Metadata-Version: 2.1
Name: PyYAML
Version: 6.0.1
Summary: YAML parser and emitter for Python
Home-page: https://pyyaml.org/
Author: Kirill Simonov
Author-email: xi@resolvent.net
License: MIT
Download-URL: https://pypi.org/project/PyYAML/
Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
Project-URL: CI, https://github.com/yaml/pyyaml/actions
Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
Project-URL: Source Code, https://github.com/yaml/pyyaml
Platform: Any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup
Requires-Python: >=3.6
YAML is a data serialization format designed for human readability
and interaction with scripting languages. PyYAML is a YAML parser
and emitter for Python.
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
support, capable extension API, and sensible error messages. PyYAML
supports standard YAML tags and provides Python-specific tags that
allow to represent an arbitrary Python object.
PyYAML is applicable for a broad range of tasks from complex
configuration files to object serialization and persistence.

43
Lib/site-packages/PyYAML-6.0.1.dist-info/RECORD

@ -0,0 +1,43 @@
_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
yaml/__init__.py,sha256=bhl05qSeO-1ZxlSRjGrvl2m9nrXb1n9-GQatTN0Mrqc,12311
yaml/_yaml.cp37-win_amd64.pyd,sha256=fDBGa8nWRKTESNlw3QVb1j56CchXC9eqZexW6hFMgl8,261632
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
PyYAML-6.0.1.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
PyYAML-6.0.1.dist-info/METADATA,sha256=fOy4MbPYqGeWtmYxczrxRS9fIXo9aBDkKzsWbM23X1M,2083
PyYAML-6.0.1.dist-info/WHEEL,sha256=bipuClDp75Tl92wOCbNQvfy8uWiHgUDTfxkDCRx_slY,101
PyYAML-6.0.1.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
PyYAML-6.0.1.dist-info/RECORD,,
PyYAML-6.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
yaml/__pycache__/composer.cpython-37.pyc,,
yaml/__pycache__/constructor.cpython-37.pyc,,
yaml/__pycache__/cyaml.cpython-37.pyc,,
yaml/__pycache__/dumper.cpython-37.pyc,,
yaml/__pycache__/emitter.cpython-37.pyc,,
yaml/__pycache__/error.cpython-37.pyc,,
yaml/__pycache__/events.cpython-37.pyc,,
yaml/__pycache__/loader.cpython-37.pyc,,
yaml/__pycache__/nodes.cpython-37.pyc,,
yaml/__pycache__/parser.cpython-37.pyc,,
yaml/__pycache__/reader.cpython-37.pyc,,
yaml/__pycache__/representer.cpython-37.pyc,,
yaml/__pycache__/resolver.cpython-37.pyc,,
yaml/__pycache__/scanner.cpython-37.pyc,,
yaml/__pycache__/serializer.cpython-37.pyc,,
yaml/__pycache__/tokens.cpython-37.pyc,,
yaml/__pycache__/__init__.cpython-37.pyc,,
_yaml/__pycache__/__init__.cpython-37.pyc,,

5
Lib/site-packages/PyYAML-6.0.1.dist-info/WHEEL

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.40.0)
Root-Is-Purelib: false
Tag: cp37-cp37m-win_amd64

2
Lib/site-packages/PyYAML-6.0.1.dist-info/top_level.txt

@ -0,0 +1,2 @@
_yaml
yaml

1
Lib/site-packages/Werkzeug-2.2.3.dist-info/INSTALLER

@ -0,0 +1 @@
pip

28
Lib/site-packages/Werkzeug-2.2.3.dist-info/LICENSE.rst

@ -0,0 +1,28 @@
Copyright 2007 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

126
Lib/site-packages/Werkzeug-2.2.3.dist-info/METADATA

@ -0,0 +1,126 @@
Metadata-Version: 2.1
Name: Werkzeug
Version: 2.2.3
Summary: The comprehensive WSGI web application library.
Home-page: https://palletsprojects.com/p/werkzeug/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://werkzeug.palletsprojects.com/
Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/werkzeug/
Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE.rst
Requires-Dist: MarkupSafe (>=2.1.1)
Provides-Extra: watchdog
Requires-Dist: watchdog ; extra == 'watchdog'
Werkzeug
========
*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff")
Werkzeug is a comprehensive `WSGI`_ web application library. It began as
a simple collection of various utilities for WSGI applications and has
become one of the most advanced WSGI utility libraries.
It includes:
- An interactive debugger that allows inspecting stack traces and
source code in the browser with an interactive interpreter for any
frame in the stack.
- A full-featured request object with objects to interact with
headers, query args, form data, files, and cookies.
- A response object that can wrap other WSGI applications and handle
streaming data.
- A routing system for matching URLs to endpoints and generating URLs
for endpoints, with an extensible system for capturing variables
from URLs.
- HTTP utilities to handle entity tags, cache control, dates, user
agents, cookies, files, and more.
- A threaded WSGI server for use while developing applications
locally.
- A test client for simulating HTTP requests during testing without
requiring running a server.
Werkzeug doesn't enforce any dependencies. It is up to the developer to
choose a template engine, database adapter, and even how to handle
requests. It can be used to build all sorts of end user applications
such as blogs, wikis, or bulletin boards.
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
providing more structure and patterns for defining powerful
applications.
.. _WSGI: https://wsgi.readthedocs.io/en/latest/
.. _Flask: https://www.palletsprojects.com/p/flask/
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U Werkzeug
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
from werkzeug.wrappers import Request, Response
@Request.application
def application(request):
return Response('Hello, World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, application)
Donate
------
The Pallets organization develops and supports Werkzeug and other
popular packages. In order to grow the community of contributors and
users, and allow the maintainers to devote more time to the projects,
`please donate today`_.
.. _please donate today: https://palletsprojects.com/donate
Links
-----
- Documentation: https://werkzeug.palletsprojects.com/
- Changes: https://werkzeug.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Werkzeug/
- Source Code: https://github.com/pallets/werkzeug/
- Issue Tracker: https://github.com/pallets/werkzeug/issues/
- Website: https://palletsprojects.com/p/werkzeug/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

98
Lib/site-packages/Werkzeug-2.2.3.dist-info/RECORD

@ -0,0 +1,98 @@
werkzeug/__init__.py,sha256=Hr0lQweC21HXPVBemSpBJUIzrbq2mn8h70J1h30QcqY,188
werkzeug/_internal.py,sha256=4lwshe63pFlCo0h2IMcmvhbugA50QXQvfLD5VoY5c4Q,16271
werkzeug/_reloader.py,sha256=hiP0z4bi6p_8UIJOtq7K0BV2dqCik5yztWLsDXeI_WE,14285
werkzeug/datastructures.py,sha256=v2WYfs1rb1OuQgXyLripHQFwgodrfTNCd5P5f8n3ueA,97081
werkzeug/datastructures.pyi,sha256=HRzDLc7A6qnwluhNqn6AT76CsLZIkAbVVqxn0AbfV-s,34506
werkzeug/exceptions.py,sha256=8-KOXguQkOLoBUdN-7x_WyHT92TcAmjTWNwG4t8QYIg,26527
werkzeug/formparser.py,sha256=DBRbbAnzspYUBzgfxPaZC7MjGAK_m5QTvdWoyvrhw4o,16516
werkzeug/http.py,sha256=NqJjYCt8tKn2XOEKPApq4L3q8zb8YFq3GFOe5gsonI4,42776
werkzeug/local.py,sha256=v-HEqr4bLpLHl4upCj97MOfUyCjW10Tp6mcNaFRFyew,22288
werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
werkzeug/security.py,sha256=7TVI0L62emBHAh-1RHB_KlwGYcE08pPCyU674Ho4aNE,4653
werkzeug/serving.py,sha256=XCiHFbMCFCgecKycgajhF4rFsGoemeN0xW1eTQqNt-g,37558
werkzeug/test.py,sha256=uMahfM81RqEN3d3Sp4SkN36Pi8oZpV6dTgFY0cW1_2c,48126
werkzeug/testapp.py,sha256=RJhT_2JweNiMKe304N3bF1zaIeMpRx-CIMERdeydfTY,9404
werkzeug/urls.py,sha256=Q9Si-eVh7yxk3rwkzrwGRm146FXVXgg9lBP3k0HUfVM,36600
werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420
werkzeug/utils.py,sha256=BDX5_7OCMVgl-ib84bCEdBG5MVvrxaSlfdg7Cxh4ND0,25174
werkzeug/wsgi.py,sha256=-VKI2iwCgLb-VToIZeBpdutkTETxy9HkIwgcFC5orkU,36060
werkzeug/debug/__init__.py,sha256=wfJ2OmljsO5C0e0sXJpTUiG6bwGU6uHtFDDDMfJfQJk,18877
werkzeug/debug/console.py,sha256=dechqiCtHfs0AQZWZofUC1S97tCuvwDgT0gdha5KwWM,6208
werkzeug/debug/repr.py,sha256=vF3TLnYBohYr8V6Gz13PTJspQs42uv3gUJSzSbmHJBo,9472
werkzeug/debug/tbtools.py,sha256=6iohJovtBSFRAcgX7_aRY4r3e19PLj3FavYB3RM4CmA,13263
werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222
werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507
werkzeug/debug/shared/debugger.js,sha256=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521
werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191
werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200
werkzeug/debug/shared/style.css,sha256=-xSxzUEZGw_IqlDR5iZxitNl8LQUjBM-_Y4UAvXVH8g,6078
werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500
werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580
werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558
werkzeug/middleware/lint.py,sha256=1w_UVKkAwq5wjjtCcDCDZwhAhWzPSZ0aDyUmbjAEeXw,13952
werkzeug/middleware/profiler.py,sha256=7pWYDYPC774S0-HYLkG3Uge58PGUMX7tWp_Cor3etvo,4883
werkzeug/middleware/proxy_fix.py,sha256=l7LC_LDu0Yd4SvUxS5SFigAJMzcIOGm6LNKl9IXJBSU,6974
werkzeug/middleware/shared_data.py,sha256=fXjrEkuqxUVLG1DLrOdQLc96QQdjftCBZ1oM5oK89h4,9528
werkzeug/routing/__init__.py,sha256=HpvahY7WwkLdV4Cq3Bsc3GrqNon4u6t8-vhbb9E5o00,4819
werkzeug/routing/converters.py,sha256=05bkekg64vLC6mqqK4ddBh589WH9yBsjtW8IJhdUBvw,6968
werkzeug/routing/exceptions.py,sha256=RklUDL9ajOv2fTcRNj4pb18Bs4Y-GKk4rIeTSfsqkks,4737
werkzeug/routing/map.py,sha256=XN4ZjzEF1SfMxtdov89SDE-1_U78KVnnobTfnHzqbmE,36757
werkzeug/routing/matcher.py,sha256=6VvQYCCOjyj1JKUZKuAiVA_U1nXtvvJ70pSbBUdL_1k,7509
werkzeug/routing/rules.py,sha256=3YsPpI9ZGcNmFiV2Go2Td1DvZ9ZdaMMnvGP1o17aMfc,31836
werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
werkzeug/sansio/http.py,sha256=k3nREBfU-r8fXCfSTKQenys25q9bzUOvdY-OVGrqztA,5107
werkzeug/sansio/multipart.py,sha256=vMZ85cvLD55clUTcTin2DtBv2GQRGh0_fExklnXKHoI,10055
werkzeug/sansio/request.py,sha256=SiGcx2cz-l81TlCCrKrT2fePqC64hN8fSg5Ig6J6vRs,20175
werkzeug/sansio/response.py,sha256=UTl-teQDDjovrZMkjj3ZQsHw-JtiFak5JfKEk1_vBYU,26026
werkzeug/sansio/utils.py,sha256=EjbqdHdT-JZWgjUQaaWSgBUIRprXUkrsMQQqJlJHpVU,4847
werkzeug/wrappers/__init__.py,sha256=kGyK7rOud3qCxll_jFyW15YarJhj1xtdf3ocx9ZheB8,120
werkzeug/wrappers/request.py,sha256=XmpTThXytTdznbPJnIsfdoIAvdi-THzTJQ9DsvARhn4,24026
werkzeug/wrappers/response.py,sha256=ii1IaN2eUfoB-tBqbn_46fCB_SVVL8Fu4qd6cu0AlEY,34963
Werkzeug-2.2.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475
Werkzeug-2.2.3.dist-info/METADATA,sha256=TIyameVEp5p52N9E1mTWWabY6g1sB0Dm25vznZQeXPQ,4416
Werkzeug-2.2.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
Werkzeug-2.2.3.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9
Werkzeug-2.2.3.dist-info/RECORD,,
Werkzeug-2.2.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
werkzeug/debug/__pycache__/console.cpython-37.pyc,,
werkzeug/debug/__pycache__/repr.cpython-37.pyc,,
werkzeug/debug/__pycache__/tbtools.cpython-37.pyc,,
werkzeug/debug/__pycache__/__init__.cpython-37.pyc,,
werkzeug/middleware/__pycache__/dispatcher.cpython-37.pyc,,
werkzeug/middleware/__pycache__/http_proxy.cpython-37.pyc,,
werkzeug/middleware/__pycache__/lint.cpython-37.pyc,,
werkzeug/middleware/__pycache__/profiler.cpython-37.pyc,,
werkzeug/middleware/__pycache__/proxy_fix.cpython-37.pyc,,
werkzeug/middleware/__pycache__/shared_data.cpython-37.pyc,,
werkzeug/middleware/__pycache__/__init__.cpython-37.pyc,,
werkzeug/routing/__pycache__/converters.cpython-37.pyc,,
werkzeug/routing/__pycache__/exceptions.cpython-37.pyc,,
werkzeug/routing/__pycache__/map.cpython-37.pyc,,
werkzeug/routing/__pycache__/matcher.cpython-37.pyc,,
werkzeug/routing/__pycache__/rules.cpython-37.pyc,,
werkzeug/routing/__pycache__/__init__.cpython-37.pyc,,
werkzeug/sansio/__pycache__/http.cpython-37.pyc,,
werkzeug/sansio/__pycache__/multipart.cpython-37.pyc,,
werkzeug/sansio/__pycache__/request.cpython-37.pyc,,
werkzeug/sansio/__pycache__/response.cpython-37.pyc,,
werkzeug/sansio/__pycache__/utils.cpython-37.pyc,,
werkzeug/sansio/__pycache__/__init__.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/request.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/response.cpython-37.pyc,,
werkzeug/wrappers/__pycache__/__init__.cpython-37.pyc,,
werkzeug/__pycache__/datastructures.cpython-37.pyc,,
werkzeug/__pycache__/exceptions.cpython-37.pyc,,
werkzeug/__pycache__/formparser.cpython-37.pyc,,
werkzeug/__pycache__/http.cpython-37.pyc,,
werkzeug/__pycache__/local.cpython-37.pyc,,
werkzeug/__pycache__/security.cpython-37.pyc,,
werkzeug/__pycache__/serving.cpython-37.pyc,,
werkzeug/__pycache__/test.cpython-37.pyc,,
werkzeug/__pycache__/testapp.cpython-37.pyc,,
werkzeug/__pycache__/urls.cpython-37.pyc,,
werkzeug/__pycache__/user_agent.cpython-37.pyc,,
werkzeug/__pycache__/utils.cpython-37.pyc,,
werkzeug/__pycache__/wsgi.cpython-37.pyc,,
werkzeug/__pycache__/_internal.cpython-37.pyc,,
werkzeug/__pycache__/_reloader.cpython-37.pyc,,
werkzeug/__pycache__/__init__.cpython-37.pyc,,

5
Lib/site-packages/Werkzeug-2.2.3.dist-info/WHEEL

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.38.4)
Root-Is-Purelib: true
Tag: py3-none-any

1
Lib/site-packages/Werkzeug-2.2.3.dist-info/top_level.txt

@ -0,0 +1 @@
werkzeug

BIN
Lib/site-packages/__pycache__/isympy.cpython-37.pyc

BIN
Lib/site-packages/__pycache__/readline.cpython-37.pyc

BIN
Lib/site-packages/__pycache__/six.cpython-37.pyc

BIN
Lib/site-packages/__pycache__/typing_extensions.cpython-37.pyc

33
Lib/site-packages/_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__ = ''

BIN
Lib/site-packages/_yaml/__pycache__/__init__.cpython-37.pyc

1
Lib/site-packages/aniso8601-9.0.1.dist-info/INSTALLER

@ -0,0 +1 @@
pip

27
Lib/site-packages/aniso8601-9.0.1.dist-info/LICENSE

@ -0,0 +1,27 @@
Copyright (c) 2021, Brandon Nielsen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

513
Lib/site-packages/aniso8601-9.0.1.dist-info/METADATA

@ -0,0 +1,513 @@
Metadata-Version: 2.1
Name: aniso8601
Version: 9.0.1
Summary: A library for parsing ISO 8601 strings.
Home-page: https://bitbucket.org/nielsenb/aniso8601
Author: Brandon Nielsen
Author-email: nielsenb@jetfuse.net
License: UNKNOWN
Project-URL: Documentation, https://aniso8601.readthedocs.io/
Project-URL: Source, https://bitbucket.org/nielsenb/aniso8601
Project-URL: Tracker, https://bitbucket.org/nielsenb/aniso8601/issues
Keywords: iso8601 parser
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Description-Content-Type: text/x-rst
Provides-Extra: dev
Requires-Dist: black ; extra == 'dev'
Requires-Dist: coverage ; extra == 'dev'
Requires-Dist: isort ; extra == 'dev'
Requires-Dist: pre-commit ; extra == 'dev'
Requires-Dist: pyenchant ; extra == 'dev'
Requires-Dist: pylint ; extra == 'dev'
aniso8601
=========
Another ISO 8601 parser for Python
----------------------------------
Features
========
* Pure Python implementation
* Logical behavior
- Parse a time, get a `datetime.time <http://docs.python.org/3/library/datetime.html#datetime.time>`_
- Parse a date, get a `datetime.date <http://docs.python.org/3/library/datetime.html#datetime.date>`_
- Parse a datetime, get a `datetime.datetime <http://docs.python.org/3/library/datetime.html#datetime.datetime>`_
- Parse a duration, get a `datetime.timedelta <http://docs.python.org/3/library/datetime.html#datetime.timedelta>`_
- Parse an interval, get a tuple of dates or datetimes
- Parse a repeating interval, get a date or datetime `generator <https://wiki.python.org/moin/Generators>`_
* UTC offset represented as fixed-offset tzinfo
* Parser separate from representation, allowing parsing to different datetime representations (see `Builders`_)
* No regular expressions
Installation
============
The recommended installation method is to use pip::
$ pip install aniso8601
Alternatively, you can download the source (git repository hosted at `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_) and install directly::
$ python setup.py install
Use
===
Parsing datetimes
-----------------
*Consider* `datetime.datetime.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat>`_ *for basic ISO 8601 datetime parsing*
To parse a typical ISO 8601 datetime string::
>>> import aniso8601
>>> aniso8601.parse_datetime('1977-06-10T12:00:00Z')
datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)
Alternative delimiters can be specified, for example, a space::
>>> aniso8601.parse_datetime('1977-06-10 12:00:00Z', delimiter=' ')
datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)
UTC offsets are supported::
>>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00')
datetime.datetime(1979, 6, 5, 8, 0, tzinfo=-8:00:00 UTC)
If a UTC offset is not specified, the returned datetime will be naive::
>>> aniso8601.parse_datetime('1983-01-22T08:00:00')
datetime.datetime(1983, 1, 22, 8, 0)
Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::
>>> aniso8601.parse_datetime('2018-03-06T23:59:60')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 196, in parse_datetime
return builder.build_datetime(datepart, timepart)
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 237, in build_datetime
cls._build_object(time))
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 336, in _build_object
return cls.build_time(hh=parsetuple.hh, mm=parsetuple.mm,
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time
hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time
raise LeapSecondError('Leap seconds are not supported.')
aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.
To get the resolution of an ISO 8601 datetime string::
>>> aniso8601.get_datetime_resolution('1977-06-10T12:00:00Z') == aniso8601.resolution.TimeResolution.Seconds
True
>>> aniso8601.get_datetime_resolution('1977-06-10T12:00') == aniso8601.resolution.TimeResolution.Minutes
True
>>> aniso8601.get_datetime_resolution('1977-06-10T12') == aniso8601.resolution.TimeResolution.Hours
True
Note that datetime resolutions map to :code:`TimeResolution` as a valid datetime must have at least one time member so the resolution mapping is equivalent.
Parsing dates
-------------
*Consider* `datetime.date.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat>`_ *for basic ISO 8601 date parsing*
To parse a date represented in an ISO 8601 string::
>>> import aniso8601
>>> aniso8601.parse_date('1984-04-23')
datetime.date(1984, 4, 23)
Basic format is supported as well::
>>> aniso8601.parse_date('19840423')
datetime.date(1984, 4, 23)
To parse a date using the ISO 8601 week date format::
>>> aniso8601.parse_date('1986-W38-1')
datetime.date(1986, 9, 15)
To parse an ISO 8601 ordinal date::
>>> aniso8601.parse_date('1988-132')
datetime.date(1988, 5, 11)
To get the resolution of an ISO 8601 date string::
>>> aniso8601.get_date_resolution('1981-04-05') == aniso8601.resolution.DateResolution.Day
True
>>> aniso8601.get_date_resolution('1981-04') == aniso8601.resolution.DateResolution.Month
True
>>> aniso8601.get_date_resolution('1981') == aniso8601.resolution.DateResolution.Year
True
Parsing times
-------------
*Consider* `datetime.time.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.time.fromisoformat>`_ *for basic ISO 8601 time parsing*
To parse a time formatted as an ISO 8601 string::
>>> import aniso8601
>>> aniso8601.parse_time('11:31:14')
datetime.time(11, 31, 14)
As with all of the above, basic format is supported::
>>> aniso8601.parse_time('113114')
datetime.time(11, 31, 14)
A UTC offset can be specified for times::
>>> aniso8601.parse_time('17:18:19-02:30')
datetime.time(17, 18, 19, tzinfo=-2:30:00 UTC)
>>> aniso8601.parse_time('171819Z')
datetime.time(17, 18, 19, tzinfo=+0:00:00 UTC)
Reduced accuracy is supported::
>>> aniso8601.parse_time('21:42')
datetime.time(21, 42)
>>> aniso8601.parse_time('22')
datetime.time(22, 0)
A decimal fraction is always allowed on the lowest order element of an ISO 8601 formatted time::
>>> aniso8601.parse_time('22:33.5')
datetime.time(22, 33, 30)
>>> aniso8601.parse_time('23.75')
datetime.time(23, 45)
The decimal fraction can be specified with a comma instead of a full-stop::
>>> aniso8601.parse_time('22:33,5')
datetime.time(22, 33, 30)
>>> aniso8601.parse_time('23,75')
datetime.time(23, 45)
Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::
>>> aniso8601.parse_time('23:59:60')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 174, in parse_time
return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz)
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time
hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time
raise LeapSecondError('Leap seconds are not supported.')
aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.
To get the resolution of an ISO 8601 time string::
>>> aniso8601.get_time_resolution('11:31:14') == aniso8601.resolution.TimeResolution.Seconds
True
>>> aniso8601.get_time_resolution('11:31') == aniso8601.resolution.TimeResolution.Minutes
True
>>> aniso8601.get_time_resolution('11') == aniso8601.resolution.TimeResolution.Hours
True
Parsing durations
-----------------
To parse a duration formatted as an ISO 8601 string::
>>> import aniso8601
>>> aniso8601.parse_duration('P1Y2M3DT4H54M6S')
datetime.timedelta(428, 17646)
Reduced accuracy is supported::
>>> aniso8601.parse_duration('P1Y')
datetime.timedelta(365)
A decimal fraction is allowed on the lowest order element::
>>> aniso8601.parse_duration('P1YT3.5M')
datetime.timedelta(365, 210)
The decimal fraction can be specified with a comma instead of a full-stop::
>>> aniso8601.parse_duration('P1YT3,5M')
datetime.timedelta(365, 210)
Parsing a duration from a combined date and time is supported as well::
>>> aniso8601.parse_duration('P0001-01-02T01:30:05')
datetime.timedelta(397, 5405)
To get the resolution of an ISO 8601 duration string::
>>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M6S') == aniso8601.resolution.DurationResolution.Seconds
True
>>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M') == aniso8601.resolution.DurationResolution.Minutes
True
>>> aniso8601.get_duration_resolution('P1Y2M3DT4H') == aniso8601.resolution.DurationResolution.Hours
True
>>> aniso8601.get_duration_resolution('P1Y2M3D') == aniso8601.resolution.DurationResolution.Days
True
>>> aniso8601.get_duration_resolution('P1Y2M') == aniso8601.resolution.DurationResolution.Months
True
>>> aniso8601.get_duration_resolution('P1Y') == aniso8601.resolution.DurationResolution.Years
True
The default :code:`PythonTimeBuilder` assumes years are 365 days, and months are 30 days. Where calendar level accuracy is required, a `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ can be used, see also `Builders`_.
Parsing intervals
-----------------
To parse an interval specified by a start and end::
>>> import aniso8601
>>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00')
(datetime.datetime(2007, 3, 1, 13, 0), datetime.datetime(2008, 5, 11, 15, 30))
Intervals specified by a start time and a duration are supported::
>>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M')
(datetime.datetime(2007, 3, 1, 13, 0, tzinfo=+0:00:00 UTC), datetime.datetime(2008, 5, 9, 15, 30, tzinfo=+0:00:00 UTC))
A duration can also be specified by a duration and end time::
>>> aniso8601.parse_interval('P1M/1981-04-05')
(datetime.date(1981, 4, 5), datetime.date(1981, 3, 6))
Notice that the result of the above parse is not in order from earliest to latest. If sorted intervals are required, simply use the :code:`sorted` keyword as shown below::
>>> sorted(aniso8601.parse_interval('P1M/1981-04-05'))
[datetime.date(1981, 3, 6), datetime.date(1981, 4, 5)]
The end of an interval is returned as a datetime when required to maintain the resolution specified by a duration, even if the duration start is given as a date::
>>> aniso8601.parse_interval('2014-11-12/PT4H54M6.5S')
(datetime.date(2014, 11, 12), datetime.datetime(2014, 11, 12, 4, 54, 6, 500000))
>>> aniso8601.parse_interval('2007-03-01/P1.5D')
(datetime.date(2007, 3, 1), datetime.datetime(2007, 3, 2, 12, 0))
Concise representations are supported::
>>> aniso8601.parse_interval('2020-01-01/02')
(datetime.date(2020, 1, 1), datetime.date(2020, 1, 2))
>>> aniso8601.parse_interval('2007-12-14T13:30/15:30')
(datetime.datetime(2007, 12, 14, 13, 30), datetime.datetime(2007, 12, 14, 15, 30))
>>> aniso8601.parse_interval('2008-02-15/03-14')
(datetime.date(2008, 2, 15), datetime.date(2008, 3, 14))
>>> aniso8601.parse_interval('2007-11-13T09:00/15T17:00')
(datetime.datetime(2007, 11, 13, 9, 0), datetime.datetime(2007, 11, 15, 17, 0))
Repeating intervals are supported as well, and return a `generator <https://wiki.python.org/moin/Generators>`_::
>>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D')
<generator object _date_generator at 0x7fd800d3b320>
>>> list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D'))
[datetime.date(1981, 4, 5), datetime.date(1981, 4, 6), datetime.date(1981, 4, 7)]
Repeating intervals are allowed to go in the reverse direction::
>>> list(aniso8601.parse_repeating_interval('R2/PT1H2M/1980-03-05T01:01:00'))
[datetime.datetime(1980, 3, 5, 1, 1), datetime.datetime(1980, 3, 4, 23, 59)]
Unbounded intervals are also allowed (Python 2)::
>>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')
>>> result.next()
datetime.datetime(1980, 3, 5, 1, 1)
>>> result.next()
datetime.datetime(1980, 3, 4, 23, 59)
or for Python 3::
>>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')
>>> next(result)
datetime.datetime(1980, 3, 5, 1, 1)
>>> next(result)
datetime.datetime(1980, 3, 4, 23, 59)
Note that you should never try to convert a generator produced by an unbounded interval to a list::
>>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 560, in _date_generator_unbounded
currentdate += timedelta
OverflowError: date value out of range
To get the resolution of an ISO 8601 interval string::
>>> aniso8601.get_interval_resolution('2007-03-01T13:00:00/2008-05-11T15:30:00') == aniso8601.resolution.IntervalResolution.Seconds
True
>>> aniso8601.get_interval_resolution('2007-03-01T13:00/2008-05-11T15:30') == aniso8601.resolution.IntervalResolution.Minutes
True
>>> aniso8601.get_interval_resolution('2007-03-01T13/2008-05-11T15') == aniso8601.resolution.IntervalResolution.Hours
True
>>> aniso8601.get_interval_resolution('2007-03-01/2008-05-11') == aniso8601.resolution.IntervalResolution.Day
True
>>> aniso8601.get_interval_resolution('2007-03/P1Y') == aniso8601.resolution.IntervalResolution.Month
True
>>> aniso8601.get_interval_resolution('2007/P1Y') == aniso8601.resolution.IntervalResolution.Year
True
And for repeating ISO 8601 interval strings::
>>> aniso8601.get_repeating_interval_resolution('R3/1981-04-05/P1D') == aniso8601.resolution.IntervalResolution.Day
True
>>> aniso8601.get_repeating_interval_resolution('R/PT1H2M/1980-03-05T01:01:00') == aniso8601.resolution.IntervalResolution.Seconds
True
Builders
========
Builders can be used to change the output format of a parse operation. All parse functions have a :code:`builder` keyword argument which accepts a builder class.
Two builders are included. The :code:`PythonTimeBuilder` (the default) in the :code:`aniso8601.builders.python` module, and the :code:`TupleBuilder` which returns the parse result as a corresponding named tuple and is located in the :code:`aniso8601.builders` module.
Information on writing a builder can be found in `BUILDERS </BUILDERS.rst>`_.
The following builders are available as separate projects:
* `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ supports parsing to `datetutil relativedelta types <https://dateutil.readthedocs.io/en/stable/relativedelta.html>`_ for calendar level accuracy
* `AttoTimeBuilder <https://bitbucket.org/nielsenb/attotimebuilder>`_ supports parsing directly to `attotime attodatetime and attotimedelta types <https://bitbucket.org/nielsenb/attotime>`_ which support sub-nanosecond precision
* `NumPyTimeBuilder <https://bitbucket.org/nielsenb/numpytimebuilder>`_ supports parsing directly to `NumPy datetime64 and timedelta64 types <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_
TupleBuilder
------------
The :code:`TupleBuilder` returns parse results as `named tuples <https://docs.python.org/3/library/collections.html#collections.namedtuple>`_. It is located in the :code:`aniso8601.builders` module.
Datetimes
^^^^^^^^^
Parsing a datetime returns a :code:`DatetimeTuple` containing :code:`Date` and :code:`Time` tuples . The date tuple contains the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`. The time tuple contains the following parse components :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` itself is a tuple with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name` with :code:`negative` and :code:`Z` being booleans::
>>> import aniso8601
>>> from aniso8601.builders import TupleBuilder
>>> aniso8601.parse_datetime('1977-06-10T12:00:00', builder=TupleBuilder)
Datetime(date=Date(YYYY='1977', MM='06', DD='10', Www=None, D=None, DDD=None), time=Time(hh='12', mm='00', ss='00', tz=None))
>>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00', builder=TupleBuilder)
Datetime(date=Date(YYYY='1979', MM='06', DD='05', Www=None, D=None, DDD=None), time=Time(hh='08', mm='00', ss='00', tz=Timezone(negative=True, Z=None, hh='08', mm='00', name='-08:00')))
Dates
^^^^^
Parsing a date returns a :code:`DateTuple` containing the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`::
>>> import aniso8601
>>> from aniso8601.builders import TupleBuilder
>>> aniso8601.parse_date('1984-04-23', builder=TupleBuilder)
Date(YYYY='1984', MM='04', DD='23', Www=None, D=None, DDD=None)
>>> aniso8601.parse_date('1986-W38-1', builder=TupleBuilder)
Date(YYYY='1986', MM=None, DD=None, Www='38', D='1', DDD=None)
>>> aniso8601.parse_date('1988-132', builder=TupleBuilder)
Date(YYYY='1988', MM=None, DD=None, Www=None, D=None, DDD='132')
Times
^^^^^
Parsing a time returns a :code:`TimeTuple` containing following parse components: :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` is a :code:`TimezoneTuple` with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name`, with :code:`negative` and :code:`Z` being booleans::
>>> import aniso8601
>>> from aniso8601.builders import TupleBuilder
>>> aniso8601.parse_time('11:31:14', builder=TupleBuilder)
Time(hh='11', mm='31', ss='14', tz=None)
>>> aniso8601.parse_time('171819Z', builder=TupleBuilder)
Time(hh='17', mm='18', ss='19', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))
>>> aniso8601.parse_time('17:18:19-02:30', builder=TupleBuilder)
Time(hh='17', mm='18', ss='19', tz=Timezone(negative=True, Z=None, hh='02', mm='30', name='-02:30'))
Durations
^^^^^^^^^
Parsing a duration returns a :code:`DurationTuple` containing the following parse components: :code:`PnY`, :code:`PnM`, :code:`PnW`, :code:`PnD`, :code:`TnH`, :code:`TnM`, :code:`TnS`::
>>> import aniso8601
>>> from aniso8601.builders import TupleBuilder
>>> aniso8601.parse_duration('P1Y2M3DT4H54M6S', builder=TupleBuilder)
Duration(PnY='1', PnM='2', PnW=None, PnD='3', TnH='4', TnM='54', TnS='6')
>>> aniso8601.parse_duration('P7W', builder=TupleBuilder)
Duration(PnY=None, PnM=None, PnW='7', PnD=None, TnH=None, TnM=None, TnS=None)
Intervals
^^^^^^^^^
Parsing an interval returns an :code:`IntervalTuple` containing the following parse components: :code:`start`, :code:`end`, :code:`duration`, :code:`start` and :code:`end` may both be datetime or date tuples, :code:`duration` is a duration tuple::
>>> import aniso8601
>>> from aniso8601.builders import TupleBuilder
>>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00', builder=TupleBuilder)
Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=None)), end=Datetime(date=Date(YYYY='2008', MM='05', DD='11', Www=None, D=None, DDD=None), time=Time(hh='15', mm='30', ss='00', tz=None)), duration=None)
>>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M', builder=TupleBuilder)
Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))), end=None, duration=Duration(PnY='1', PnM='2', PnW=None, PnD='10', TnH='2', TnM='30', TnS=None))
>>> aniso8601.parse_interval('P1M/1981-04-05', builder=TupleBuilder)
Interval(start=None, end=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), duration=Duration(PnY=None, PnM='1', PnW=None, PnD=None, TnH=None, TnM=None, TnS=None))
A repeating interval returns a :code:`RepeatingIntervalTuple` containing the following parse components: :code:`R`, :code:`Rnn`, :code:`interval`, where :code:`R` is a boolean, :code:`True` for an unbounded interval, :code:`False` otherwise.::
>>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D', builder=TupleBuilder)
RepeatingInterval(R=False, Rnn='3', interval=Interval(start=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), end=None, duration=Duration(PnY=None, PnM=None, PnW=None, PnD='1', TnH=None, TnM=None, TnS=None)))
>>> aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00', builder=TupleBuilder)
RepeatingInterval(R=True, Rnn=None, interval=Interval(start=None, end=Datetime(date=Date(YYYY='1980', MM='03', DD='05', Www=None, D=None, DDD=None), time=Time(hh='01', mm='01', ss='00', tz=None)), duration=Duration(PnY=None, PnM=None, PnW=None, PnD=None, TnH='1', TnM='2', TnS=None)))
Development
===========
Setup
-----
It is recommended to develop using a `virtualenv <https://virtualenv.pypa.io/en/stable/>`_.
Inside a virtualenv, development dependencies can be installed automatically::
$ pip install -e .[dev]
`pre-commit <https://pre-commit.com/>`_ is used for managing pre-commit hooks::
$ pre-commit install
To run the pre-commit hooks manually::
$ pre-commit run --all-files
Tests
-----
Tests can be run using the `unittest testing framework <https://docs.python.org/3/library/unittest.html>`_::
$ python -m unittest discover aniso8601
Contributing
============
aniso8601 is an open source project hosted on `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_.
Any and all bugs are welcome on our `issue tracker <https://bitbucket.org/nielsenb/aniso8601/issues>`_.
Of particular interest are valid ISO 8601 strings that don't parse, or invalid ones that do. At a minimum,
bug reports should include an example of the misbehaving string, as well as the expected result. Of course
patches containing unit tests (or fixed bugs) are welcome!
References
==========
* `ISO 8601:2004(E) <http://dotat.at/tmp/ISO_8601-2004_E.pdf>`_ (Caution, PDF link)
* `Wikipedia article on ISO 8601 <http://en.wikipedia.org/wiki/Iso8601>`_
* `Discussion on alternative ISO 8601 parsers for Python <https://groups.google.com/forum/#!topic/comp.lang.python/Q2w4R89Nq1w>`_

60
Lib/site-packages/aniso8601-9.0.1.dist-info/RECORD

@ -0,0 +1,60 @@
aniso8601/__init__.py,sha256=tHN7Nq-3I79PLzKkBuWun_UKgolnDrn7ISO8s1HlMdo,704
aniso8601/compat.py,sha256=2exJsHW2DAxt_D2_mGj5mv0HCSMFeAAkPyFAM-ZFrA0,571
aniso8601/date.py,sha256=IDn_kqeZshllwr4pICUNZhjbqSVVlYTyHmBOgp2MlNE,4475
aniso8601/decimalfraction.py,sha256=EtwqSZJTtsQlu05m2guolhii5N1yN4dVv0v1zCZhiyk,333
aniso8601/duration.py,sha256=6AAl9A-WM2Io898peIz9xbwOvxcLc6WYGUdkYuQlTU8,9583
aniso8601/exceptions.py,sha256=-zrdcKocZhzhl71HhgVKXWF481XDWO3UhinbcycCzPU,1313
aniso8601/interval.py,sha256=7e5wICHdF2gTeFluPxBrzaA4-_5b78QzXC62DSnNzlM,10763
aniso8601/resolution.py,sha256=ee7GxL865D0dJL70TsXScz4Kzo_dwMORNvfuyCXdsgI,684
aniso8601/time.py,sha256=9IRsCERfEl_SnBBUIOR8E43XFD7Y2EqhowjiCcfinb0,5688
aniso8601/timezone.py,sha256=5_LRd_pYd08i2hmXsn_1tTUxKOI4caSvxci-VByHCWU,2134
aniso8601/utcoffset.py,sha256=8Gh8WNk_q9ELLEFZLMPbMESH-yqcoNFjul7VcpHq_1Q,2423
aniso8601/builders/__init__.py,sha256=sJanTP5Lo0lRxpLa5VKVBS9u6ZP8R1VRgozx5uSUMUU,17958
aniso8601/builders/python.py,sha256=I0RhPY2syncaMwYRVJxM6ct-O_5MHnNFY3dcF6wvy0Y,22122
aniso8601/builders/tests/__init__.py,sha256=XWM00Wzg9EZkSKyy3IW18Z8TiXfCbJS-XJNFVuylvuU,209
aniso8601/builders/tests/test_init.py,sha256=wnDhjyb5iBt9l_zTXT96uqXus-igSqn5Kn_rqX_NSHA,29997
aniso8601/builders/tests/test_python.py,sha256=pNr3lwfBKVSUQKc5BPmwCiCTpP_063WpOM-canDz4J8,61593
aniso8601/tests/__init__.py,sha256=XWM00Wzg9EZkSKyy3IW18Z8TiXfCbJS-XJNFVuylvuU,209
aniso8601/tests/compat.py,sha256=9HJqKvl0PIFBjePUgT-1eMGkA9tlESX0wNDkPvV7GOk,346
aniso8601/tests/test_compat.py,sha256=2oFOFLKTfOJIMbLjkeVhrkxSDMjE0wM-NB86SJ6st5g,763
aniso8601/tests/test_date.py,sha256=3AWmIHTS2sxm9_ZUYcI2w9ALJOYnHkkYEwlD1VW90iQ,8960
aniso8601/tests/test_decimalfraction.py,sha256=T4R_SY24DW30YuQkyofxvAmngTuXtsmwd77pF25QAlc,578
aniso8601/tests/test_duration.py,sha256=ZqUxodLrDBZ1GZWutFXjktAFHYS1hidxLclIGZP7aSA,44952
aniso8601/tests/test_init.py,sha256=GazCeGTv-OFocCx9Cck04b-c1cWiiRnqhGwoGgm4Y1Q,1689
aniso8601/tests/test_interval.py,sha256=lTg-E1vW1xmgwiWfHHwJDJ25AogSR-1p-0L4O2gQKQw,60457
aniso8601/tests/test_time.py,sha256=HLutGVdg2_HHU51U2eEEZ9UNwljrQBPU_PtX8JrdVV0,19147
aniso8601/tests/test_timezone.py,sha256=Shw7-fcUJZAbH7diCx37iXZ4VZEH45lqIgMJvoQQhtQ,4649
aniso8601/tests/test_utcoffset.py,sha256=fRNuiz3WPMrHtrdMGK3HOuZRYd68hR-VNldbwVG-cDA,1926
aniso8601-9.0.1.dist-info/LICENSE,sha256=Z_-MGC_A4Nc1cViNi8B5tOSmJKknTE4mqSPeIxDTvSk,1501
aniso8601-9.0.1.dist-info/METADATA,sha256=8x7vpReMZppobPRH8Q564bwHL9XFsgCFR3TKrHhfkjE,23431
aniso8601-9.0.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
aniso8601-9.0.1.dist-info/top_level.txt,sha256=MVQomyeED8nGIH7PUQdMzxgLppIB48oYHtcmL17ETB0,10
aniso8601-9.0.1.dist-info/RECORD,,
aniso8601-9.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
aniso8601/builders/tests/__pycache__/test_init.cpython-37.pyc,,
aniso8601/builders/tests/__pycache__/test_python.cpython-37.pyc,,
aniso8601/builders/tests/__pycache__/__init__.cpython-37.pyc,,
aniso8601/builders/__pycache__/python.cpython-37.pyc,,
aniso8601/builders/__pycache__/__init__.cpython-37.pyc,,
aniso8601/tests/__pycache__/compat.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_compat.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_date.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_decimalfraction.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_duration.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_init.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_interval.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_time.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_timezone.cpython-37.pyc,,
aniso8601/tests/__pycache__/test_utcoffset.cpython-37.pyc,,
aniso8601/tests/__pycache__/__init__.cpython-37.pyc,,
aniso8601/__pycache__/compat.cpython-37.pyc,,
aniso8601/__pycache__/date.cpython-37.pyc,,
aniso8601/__pycache__/decimalfraction.cpython-37.pyc,,
aniso8601/__pycache__/duration.cpython-37.pyc,,
aniso8601/__pycache__/exceptions.cpython-37.pyc,,
aniso8601/__pycache__/interval.cpython-37.pyc,,
aniso8601/__pycache__/resolution.cpython-37.pyc,,
aniso8601/__pycache__/time.cpython-37.pyc,,
aniso8601/__pycache__/timezone.cpython-37.pyc,,
aniso8601/__pycache__/utcoffset.cpython-37.pyc,,
aniso8601/__pycache__/__init__.cpython-37.pyc,,

6
Lib/site-packages/aniso8601-9.0.1.dist-info/WHEEL

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.36.2)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

1
Lib/site-packages/aniso8601-9.0.1.dist-info/top_level.txt

@ -0,0 +1 @@
aniso8601

26
Lib/site-packages/aniso8601/__init__.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
from aniso8601.date import get_date_resolution, parse_date
from aniso8601.duration import get_duration_resolution, parse_duration
from aniso8601.interval import (
get_interval_resolution,
get_repeating_interval_resolution,
parse_interval,
parse_repeating_interval,
)
# Import the main parsing functions so they are readily available
from aniso8601.time import (
get_datetime_resolution,
get_time_resolution,
parse_datetime,
parse_time,
)
__version__ = "9.0.1"

BIN
Lib/site-packages/aniso8601/__pycache__/__init__.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/compat.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/date.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/decimalfraction.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/duration.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/exceptions.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/interval.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/resolution.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/time.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/timezone.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/__pycache__/utcoffset.cpython-37.pyc

614
Lib/site-packages/aniso8601/builders/__init__.py

@ -0,0 +1,614 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import calendar
from collections import namedtuple
from aniso8601.exceptions import (
DayOutOfBoundsError,
HoursOutOfBoundsError,
ISOFormatError,
LeapSecondError,
MidnightBoundsError,
MinutesOutOfBoundsError,
MonthOutOfBoundsError,
SecondsOutOfBoundsError,
WeekOutOfBoundsError,
YearOutOfBoundsError,
)
DateTuple = namedtuple("Date", ["YYYY", "MM", "DD", "Www", "D", "DDD"])
TimeTuple = namedtuple("Time", ["hh", "mm", "ss", "tz"])
DatetimeTuple = namedtuple("Datetime", ["date", "time"])
DurationTuple = namedtuple(
"Duration", ["PnY", "PnM", "PnW", "PnD", "TnH", "TnM", "TnS"]
)
IntervalTuple = namedtuple("Interval", ["start", "end", "duration"])
RepeatingIntervalTuple = namedtuple("RepeatingInterval", ["R", "Rnn", "interval"])
TimezoneTuple = namedtuple("Timezone", ["negative", "Z", "hh", "mm", "name"])
Limit = namedtuple(
"Limit",
[
"casterrorstring",
"min",
"max",
"rangeexception",
"rangeerrorstring",
"rangefunc",
],
)
def cast(
value,
castfunction,
caughtexceptions=(ValueError,),
thrownexception=ISOFormatError,
thrownmessage=None,
):
try:
result = castfunction(value)
except caughtexceptions:
raise thrownexception(thrownmessage)
return result
def range_check(valuestr, limit):
# Returns cast value if in range, raises defined exceptions on failure
if valuestr is None:
return None
if "." in valuestr:
castfunc = float
else:
castfunc = int
value = cast(valuestr, castfunc, thrownmessage=limit.casterrorstring)
if limit.min is not None and value < limit.min:
raise limit.rangeexception(limit.rangeerrorstring)
if limit.max is not None and value > limit.max:
raise limit.rangeexception(limit.rangeerrorstring)
return value
class BaseTimeBuilder(object):
# Limit tuple format cast function, cast error string,
# lower limit, upper limit, limit error string
DATE_YYYY_LIMIT = Limit(
"Invalid year string.",
0000,
9999,
YearOutOfBoundsError,
"Year must be between 1..9999.",
range_check,
)
DATE_MM_LIMIT = Limit(
"Invalid month string.",
1,
12,
MonthOutOfBoundsError,
"Month must be between 1..12.",
range_check,
)
DATE_DD_LIMIT = Limit(
"Invalid day string.",
1,
31,
DayOutOfBoundsError,
"Day must be between 1..31.",
range_check,
)
DATE_WWW_LIMIT = Limit(
"Invalid week string.",
1,
53,
WeekOutOfBoundsError,
"Week number must be between 1..53.",
range_check,
)
DATE_D_LIMIT = Limit(
"Invalid weekday string.",
1,
7,
DayOutOfBoundsError,
"Weekday number must be between 1..7.",
range_check,
)
DATE_DDD_LIMIT = Limit(
"Invalid ordinal day string.",
1,
366,
DayOutOfBoundsError,
"Ordinal day must be between 1..366.",
range_check,
)
TIME_HH_LIMIT = Limit(
"Invalid hour string.",
0,
24,
HoursOutOfBoundsError,
"Hour must be between 0..24 with " "24 representing midnight.",
range_check,
)
TIME_MM_LIMIT = Limit(
"Invalid minute string.",
0,
59,
MinutesOutOfBoundsError,
"Minute must be between 0..59.",
range_check,
)
TIME_SS_LIMIT = Limit(
"Invalid second string.",
0,
60,
SecondsOutOfBoundsError,
"Second must be between 0..60 with " "60 representing a leap second.",
range_check,
)
TZ_HH_LIMIT = Limit(
"Invalid timezone hour string.",
0,
23,
HoursOutOfBoundsError,
"Hour must be between 0..23.",
range_check,
)
TZ_MM_LIMIT = Limit(
"Invalid timezone minute string.",
0,
59,
MinutesOutOfBoundsError,
"Minute must be between 0..59.",
range_check,
)
DURATION_PNY_LIMIT = Limit(
"Invalid year duration string.",
0,
None,
ISOFormatError,
"Duration years component must be positive.",
range_check,
)
DURATION_PNM_LIMIT = Limit(
"Invalid month duration string.",
0,
None,
ISOFormatError,
"Duration months component must be positive.",
range_check,
)
DURATION_PNW_LIMIT = Limit(
"Invalid week duration string.",
0,
None,
ISOFormatError,
"Duration weeks component must be positive.",
range_check,
)
DURATION_PND_LIMIT = Limit(
"Invalid day duration string.",
0,
None,
ISOFormatError,
"Duration days component must be positive.",
range_check,
)
DURATION_TNH_LIMIT = Limit(
"Invalid hour duration string.",
0,
None,
ISOFormatError,
"Duration hours component must be positive.",
range_check,
)
DURATION_TNM_LIMIT = Limit(
"Invalid minute duration string.",
0,
None,
ISOFormatError,
"Duration minutes component must be positive.",
range_check,
)
DURATION_TNS_LIMIT = Limit(
"Invalid second duration string.",
0,
None,
ISOFormatError,
"Duration seconds component must be positive.",
range_check,
)
INTERVAL_RNN_LIMIT = Limit(
"Invalid duration repetition string.",
0,
None,
ISOFormatError,
"Duration repetition count must be positive.",
range_check,
)
DATE_RANGE_DICT = {
"YYYY": DATE_YYYY_LIMIT,
"MM": DATE_MM_LIMIT,
"DD": DATE_DD_LIMIT,
"Www": DATE_WWW_LIMIT,
"D": DATE_D_LIMIT,
"DDD": DATE_DDD_LIMIT,
}
TIME_RANGE_DICT = {"hh": TIME_HH_LIMIT, "mm": TIME_MM_LIMIT, "ss": TIME_SS_LIMIT}
DURATION_RANGE_DICT = {
"PnY": DURATION_PNY_LIMIT,
"PnM": DURATION_PNM_LIMIT,
"PnW": DURATION_PNW_LIMIT,
"PnD": DURATION_PND_LIMIT,
"TnH": DURATION_TNH_LIMIT,
"TnM": DURATION_TNM_LIMIT,
"TnS": DURATION_TNS_LIMIT,
}
REPEATING_INTERVAL_RANGE_DICT = {"Rnn": INTERVAL_RNN_LIMIT}
TIMEZONE_RANGE_DICT = {"hh": TZ_HH_LIMIT, "mm": TZ_MM_LIMIT}
LEAP_SECONDS_SUPPORTED = False
@classmethod
def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None):
raise NotImplementedError
@classmethod
def build_time(cls, hh=None, mm=None, ss=None, tz=None):
raise NotImplementedError
@classmethod
def build_datetime(cls, date, time):
raise NotImplementedError
@classmethod
def build_duration(
cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None
):
raise NotImplementedError
@classmethod
def build_interval(cls, start=None, end=None, duration=None):
# start, end, and duration are all tuples
raise NotImplementedError
@classmethod
def build_repeating_interval(cls, R=None, Rnn=None, interval=None):
# interval is a tuple
raise NotImplementedError
@classmethod
def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""):
raise NotImplementedError
@classmethod
def range_check_date(
cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None, rangedict=None
):
if rangedict is None:
rangedict = cls.DATE_RANGE_DICT
if "YYYY" in rangedict:
YYYY = rangedict["YYYY"].rangefunc(YYYY, rangedict["YYYY"])
if "MM" in rangedict:
MM = rangedict["MM"].rangefunc(MM, rangedict["MM"])
if "DD" in rangedict:
DD = rangedict["DD"].rangefunc(DD, rangedict["DD"])
if "Www" in rangedict:
Www = rangedict["Www"].rangefunc(Www, rangedict["Www"])
if "D" in rangedict:
D = rangedict["D"].rangefunc(D, rangedict["D"])
if "DDD" in rangedict:
DDD = rangedict["DDD"].rangefunc(DDD, rangedict["DDD"])
if DD is not None:
# Check calendar
if DD > calendar.monthrange(YYYY, MM)[1]:
raise DayOutOfBoundsError(
"{0} is out of range for {1}-{2}".format(DD, YYYY, MM)
)
if DDD is not None:
if calendar.isleap(YYYY) is False and DDD == 366:
raise DayOutOfBoundsError(
"{0} is only valid for leap year.".format(DDD)
)
return (YYYY, MM, DD, Www, D, DDD)
@classmethod
def range_check_time(cls, hh=None, mm=None, ss=None, tz=None, rangedict=None):
# Used for midnight and leap second handling
midnight = False # Handle hh = '24' specially
if rangedict is None:
rangedict = cls.TIME_RANGE_DICT
if "hh" in rangedict:
try:
hh = rangedict["hh"].rangefunc(hh, rangedict["hh"])
except HoursOutOfBoundsError as e:
if float(hh) > 24 and float(hh) < 25:
raise MidnightBoundsError("Hour 24 may only represent midnight.")
raise e
if "mm" in rangedict:
mm = rangedict["mm"].rangefunc(mm, rangedict["mm"])
if "ss" in rangedict:
ss = rangedict["ss"].rangefunc(ss, rangedict["ss"])
if hh is not None and hh == 24:
midnight = True
# Handle midnight range
if midnight is True and (
(mm is not None and mm != 0) or (ss is not None and ss != 0)
):
raise MidnightBoundsError("Hour 24 may only represent midnight.")
if cls.LEAP_SECONDS_SUPPORTED is True:
if hh != 23 and mm != 59 and ss == 60:
raise cls.TIME_SS_LIMIT.rangeexception(
cls.TIME_SS_LIMIT.rangeerrorstring
)
else:
if hh == 23 and mm == 59 and ss == 60:
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
raise LeapSecondError("Leap seconds are not supported.")
if ss == 60:
raise cls.TIME_SS_LIMIT.rangeexception(
cls.TIME_SS_LIMIT.rangeerrorstring
)
return (hh, mm, ss, tz)
@classmethod
def range_check_duration(
cls,
PnY=None,
PnM=None,
PnW=None,
PnD=None,
TnH=None,
TnM=None,
TnS=None,
rangedict=None,
):
if rangedict is None:
rangedict = cls.DURATION_RANGE_DICT
if "PnY" in rangedict:
PnY = rangedict["PnY"].rangefunc(PnY, rangedict["PnY"])
if "PnM" in rangedict:
PnM = rangedict["PnM"].rangefunc(PnM, rangedict["PnM"])
if "PnW" in rangedict:
PnW = rangedict["PnW"].rangefunc(PnW, rangedict["PnW"])
if "PnD" in rangedict:
PnD = rangedict["PnD"].rangefunc(PnD, rangedict["PnD"])
if "TnH" in rangedict:
TnH = rangedict["TnH"].rangefunc(TnH, rangedict["TnH"])
if "TnM" in rangedict:
TnM = rangedict["TnM"].rangefunc(TnM, rangedict["TnM"])
if "TnS" in rangedict:
TnS = rangedict["TnS"].rangefunc(TnS, rangedict["TnS"])
return (PnY, PnM, PnW, PnD, TnH, TnM, TnS)
@classmethod
def range_check_repeating_interval(
cls, R=None, Rnn=None, interval=None, rangedict=None
):
if rangedict is None:
rangedict = cls.REPEATING_INTERVAL_RANGE_DICT
if "Rnn" in rangedict:
Rnn = rangedict["Rnn"].rangefunc(Rnn, rangedict["Rnn"])
return (R, Rnn, interval)
@classmethod
def range_check_timezone(
cls, negative=None, Z=None, hh=None, mm=None, name="", rangedict=None
):
if rangedict is None:
rangedict = cls.TIMEZONE_RANGE_DICT
if "hh" in rangedict:
hh = rangedict["hh"].rangefunc(hh, rangedict["hh"])
if "mm" in rangedict:
mm = rangedict["mm"].rangefunc(mm, rangedict["mm"])
return (negative, Z, hh, mm, name)
@classmethod
def _build_object(cls, parsetuple):
# Given a TupleBuilder tuple, build the correct object
if type(parsetuple) is DateTuple:
return cls.build_date(
YYYY=parsetuple.YYYY,
MM=parsetuple.MM,
DD=parsetuple.DD,
Www=parsetuple.Www,
D=parsetuple.D,
DDD=parsetuple.DDD,
)
if type(parsetuple) is TimeTuple:
return cls.build_time(
hh=parsetuple.hh, mm=parsetuple.mm, ss=parsetuple.ss, tz=parsetuple.tz
)
if type(parsetuple) is DatetimeTuple:
return cls.build_datetime(parsetuple.date, parsetuple.time)
if type(parsetuple) is DurationTuple:
return cls.build_duration(
PnY=parsetuple.PnY,
PnM=parsetuple.PnM,
PnW=parsetuple.PnW,
PnD=parsetuple.PnD,
TnH=parsetuple.TnH,
TnM=parsetuple.TnM,
TnS=parsetuple.TnS,
)
if type(parsetuple) is IntervalTuple:
return cls.build_interval(
start=parsetuple.start, end=parsetuple.end, duration=parsetuple.duration
)
if type(parsetuple) is RepeatingIntervalTuple:
return cls.build_repeating_interval(
R=parsetuple.R, Rnn=parsetuple.Rnn, interval=parsetuple.interval
)
return cls.build_timezone(
negative=parsetuple.negative,
Z=parsetuple.Z,
hh=parsetuple.hh,
mm=parsetuple.mm,
name=parsetuple.name,
)
@classmethod
def _is_interval_end_concise(cls, endtuple):
if type(endtuple) is TimeTuple:
return True
if type(endtuple) is DatetimeTuple:
enddatetuple = endtuple.date
else:
enddatetuple = endtuple
if enddatetuple.YYYY is None:
return True
return False
@classmethod
def _combine_concise_interval_tuples(cls, starttuple, conciseendtuple):
starttimetuple = None
startdatetuple = None
endtimetuple = None
enddatetuple = None
if type(starttuple) is DateTuple:
startdatetuple = starttuple
else:
# Start is a datetime
starttimetuple = starttuple.time
startdatetuple = starttuple.date
if type(conciseendtuple) is DateTuple:
enddatetuple = conciseendtuple
elif type(conciseendtuple) is DatetimeTuple:
enddatetuple = conciseendtuple.date
endtimetuple = conciseendtuple.time
else:
# Time
endtimetuple = conciseendtuple
if enddatetuple is not None:
if enddatetuple.YYYY is None and enddatetuple.MM is None:
newenddatetuple = DateTuple(
YYYY=startdatetuple.YYYY,
MM=startdatetuple.MM,
DD=enddatetuple.DD,
Www=enddatetuple.Www,
D=enddatetuple.D,
DDD=enddatetuple.DDD,
)
else:
newenddatetuple = DateTuple(
YYYY=startdatetuple.YYYY,
MM=enddatetuple.MM,
DD=enddatetuple.DD,
Www=enddatetuple.Www,
D=enddatetuple.D,
DDD=enddatetuple.DDD,
)
if (starttimetuple is not None and starttimetuple.tz is not None) and (
endtimetuple is not None and endtimetuple.tz != starttimetuple.tz
):
# Copy the timezone across
endtimetuple = TimeTuple(
hh=endtimetuple.hh,
mm=endtimetuple.mm,
ss=endtimetuple.ss,
tz=starttimetuple.tz,
)
if enddatetuple is not None and endtimetuple is None:
return newenddatetuple
if enddatetuple is not None and endtimetuple is not None:
return TupleBuilder.build_datetime(newenddatetuple, endtimetuple)
return TupleBuilder.build_datetime(startdatetuple, endtimetuple)
class TupleBuilder(BaseTimeBuilder):
# Builder used to return the arguments as a tuple, cleans up some parse methods
@classmethod
def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None):
return DateTuple(YYYY, MM, DD, Www, D, DDD)
@classmethod
def build_time(cls, hh=None, mm=None, ss=None, tz=None):
return TimeTuple(hh, mm, ss, tz)
@classmethod
def build_datetime(cls, date, time):
return DatetimeTuple(date, time)
@classmethod
def build_duration(
cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None
):
return DurationTuple(PnY, PnM, PnW, PnD, TnH, TnM, TnS)
@classmethod
def build_interval(cls, start=None, end=None, duration=None):
return IntervalTuple(start, end, duration)
@classmethod
def build_repeating_interval(cls, R=None, Rnn=None, interval=None):
return RepeatingIntervalTuple(R, Rnn, interval)
@classmethod
def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""):
return TimezoneTuple(negative, Z, hh, mm, name)

BIN
Lib/site-packages/aniso8601/builders/__pycache__/__init__.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/builders/__pycache__/python.cpython-37.pyc

705
Lib/site-packages/aniso8601/builders/python.py

@ -0,0 +1,705 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import datetime
from collections import namedtuple
from functools import partial
from aniso8601.builders import (
BaseTimeBuilder,
DatetimeTuple,
DateTuple,
Limit,
TimeTuple,
TupleBuilder,
cast,
range_check,
)
from aniso8601.exceptions import (
DayOutOfBoundsError,
HoursOutOfBoundsError,
ISOFormatError,
LeapSecondError,
MidnightBoundsError,
MinutesOutOfBoundsError,
MonthOutOfBoundsError,
SecondsOutOfBoundsError,
WeekOutOfBoundsError,
YearOutOfBoundsError,
)
from aniso8601.utcoffset import UTCOffset
DAYS_PER_YEAR = 365
DAYS_PER_MONTH = 30
DAYS_PER_WEEK = 7
HOURS_PER_DAY = 24
MINUTES_PER_HOUR = 60
MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY
SECONDS_PER_MINUTE = 60
SECONDS_PER_DAY = MINUTES_PER_DAY * SECONDS_PER_MINUTE
MICROSECONDS_PER_SECOND = int(1e6)
MICROSECONDS_PER_MINUTE = 60 * MICROSECONDS_PER_SECOND
MICROSECONDS_PER_HOUR = 60 * MICROSECONDS_PER_MINUTE
MICROSECONDS_PER_DAY = 24 * MICROSECONDS_PER_HOUR
MICROSECONDS_PER_WEEK = 7 * MICROSECONDS_PER_DAY
MICROSECONDS_PER_MONTH = DAYS_PER_MONTH * MICROSECONDS_PER_DAY
MICROSECONDS_PER_YEAR = DAYS_PER_YEAR * MICROSECONDS_PER_DAY
TIMEDELTA_MAX_DAYS = datetime.timedelta.max.days
FractionalComponent = namedtuple(
"FractionalComponent", ["principal", "microsecondremainder"]
)
def year_range_check(valuestr, limit):
YYYYstr = valuestr
# Truncated dates, like '19', refer to 1900-1999 inclusive,
# we simply parse to 1900
if len(valuestr) < 4:
# Shift 0s in from the left to form complete year
YYYYstr = valuestr.ljust(4, "0")
return range_check(YYYYstr, limit)
def fractional_range_check(conversion, valuestr, limit):
if valuestr is None:
return None
if "." in valuestr:
castfunc = partial(_cast_to_fractional_component, conversion)
else:
castfunc = int
value = cast(valuestr, castfunc, thrownmessage=limit.casterrorstring)
if type(value) is FractionalComponent:
tocheck = float(valuestr)
else:
tocheck = int(valuestr)
if limit.min is not None and tocheck < limit.min:
raise limit.rangeexception(limit.rangeerrorstring)
if limit.max is not None and tocheck > limit.max:
raise limit.rangeexception(limit.rangeerrorstring)
return value
def _cast_to_fractional_component(conversion, floatstr):
# Splits a string with a decimal point into an int, and
# int representing the floating point remainder as a number
# of microseconds, determined by multiplying by conversion
intpart, floatpart = floatstr.split(".")
intvalue = int(intpart)
preconvertedvalue = int(floatpart)
convertedvalue = (preconvertedvalue * conversion) // (10 ** len(floatpart))
return FractionalComponent(intvalue, convertedvalue)
class PythonTimeBuilder(BaseTimeBuilder):
# 0000 (1 BC) is not representable as a Python date
DATE_YYYY_LIMIT = Limit(
"Invalid year string.",
datetime.MINYEAR,
datetime.MAXYEAR,
YearOutOfBoundsError,
"Year must be between {0}..{1}.".format(datetime.MINYEAR, datetime.MAXYEAR),
year_range_check,
)
TIME_HH_LIMIT = Limit(
"Invalid hour string.",
0,
24,
HoursOutOfBoundsError,
"Hour must be between 0..24 with " "24 representing midnight.",
partial(fractional_range_check, MICROSECONDS_PER_HOUR),
)
TIME_MM_LIMIT = Limit(
"Invalid minute string.",
0,
59,
MinutesOutOfBoundsError,
"Minute must be between 0..59.",
partial(fractional_range_check, MICROSECONDS_PER_MINUTE),
)
TIME_SS_LIMIT = Limit(
"Invalid second string.",
0,
60,
SecondsOutOfBoundsError,
"Second must be between 0..60 with " "60 representing a leap second.",
partial(fractional_range_check, MICROSECONDS_PER_SECOND),
)
DURATION_PNY_LIMIT = Limit(
"Invalid year duration string.",
None,
None,
YearOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_YEAR),
)
DURATION_PNM_LIMIT = Limit(
"Invalid month duration string.",
None,
None,
MonthOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_MONTH),
)
DURATION_PNW_LIMIT = Limit(
"Invalid week duration string.",
None,
None,
WeekOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_WEEK),
)
DURATION_PND_LIMIT = Limit(
"Invalid day duration string.",
None,
None,
DayOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_DAY),
)
DURATION_TNH_LIMIT = Limit(
"Invalid hour duration string.",
None,
None,
HoursOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_HOUR),
)
DURATION_TNM_LIMIT = Limit(
"Invalid minute duration string.",
None,
None,
MinutesOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_MINUTE),
)
DURATION_TNS_LIMIT = Limit(
"Invalid second duration string.",
None,
None,
SecondsOutOfBoundsError,
None,
partial(fractional_range_check, MICROSECONDS_PER_SECOND),
)
DATE_RANGE_DICT = BaseTimeBuilder.DATE_RANGE_DICT
DATE_RANGE_DICT["YYYY"] = DATE_YYYY_LIMIT
TIME_RANGE_DICT = {"hh": TIME_HH_LIMIT, "mm": TIME_MM_LIMIT, "ss": TIME_SS_LIMIT}
DURATION_RANGE_DICT = {
"PnY": DURATION_PNY_LIMIT,
"PnM": DURATION_PNM_LIMIT,
"PnW": DURATION_PNW_LIMIT,
"PnD": DURATION_PND_LIMIT,
"TnH": DURATION_TNH_LIMIT,
"TnM": DURATION_TNM_LIMIT,
"TnS": DURATION_TNS_LIMIT,
}
@classmethod
def build_date(cls, YYYY=None, MM=None, DD=None, Www=None, D=None, DDD=None):
YYYY, MM, DD, Www, D, DDD = cls.range_check_date(YYYY, MM, DD, Www, D, DDD)
if MM is None:
MM = 1
if DD is None:
DD = 1
if DDD is not None:
return PythonTimeBuilder._build_ordinal_date(YYYY, DDD)
if Www is not None:
return PythonTimeBuilder._build_week_date(YYYY, Www, isoday=D)
return datetime.date(YYYY, MM, DD)
@classmethod
def build_time(cls, hh=None, mm=None, ss=None, tz=None):
# Builds a time from the given parts, handling fractional arguments
# where necessary
hours = 0
minutes = 0
seconds = 0
microseconds = 0
hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)
if type(hh) is FractionalComponent:
hours = hh.principal
microseconds = hh.microsecondremainder
elif hh is not None:
hours = hh
if type(mm) is FractionalComponent:
minutes = mm.principal
microseconds = mm.microsecondremainder
elif mm is not None:
minutes = mm
if type(ss) is FractionalComponent:
seconds = ss.principal
microseconds = ss.microsecondremainder
elif ss is not None:
seconds = ss
(
hours,
minutes,
seconds,
microseconds,
) = PythonTimeBuilder._distribute_microseconds(
microseconds,
(hours, minutes, seconds),
(MICROSECONDS_PER_HOUR, MICROSECONDS_PER_MINUTE, MICROSECONDS_PER_SECOND),
)
# Move midnight into range
if hours == 24:
hours = 0
# Datetimes don't handle fractional components, so we use a timedelta
if tz is not None:
return (
datetime.datetime(
1, 1, 1, hour=hours, minute=minutes, tzinfo=cls._build_object(tz)
)
+ datetime.timedelta(seconds=seconds, microseconds=microseconds)
).timetz()
return (
datetime.datetime(1, 1, 1, hour=hours, minute=minutes)
+ datetime.timedelta(seconds=seconds, microseconds=microseconds)
).time()
@classmethod
def build_datetime(cls, date, time):
return datetime.datetime.combine(
cls._build_object(date), cls._build_object(time)
)
@classmethod
def build_duration(
cls, PnY=None, PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None
):
# PnY and PnM will be distributed to PnD, microsecond remainder to TnS
PnY, PnM, PnW, PnD, TnH, TnM, TnS = cls.range_check_duration(
PnY, PnM, PnW, PnD, TnH, TnM, TnS
)
seconds = TnS.principal
microseconds = TnS.microsecondremainder
return datetime.timedelta(
days=PnD,
seconds=seconds,
microseconds=microseconds,
minutes=TnM,
hours=TnH,
weeks=PnW,
)
@classmethod
def build_interval(cls, start=None, end=None, duration=None):
start, end, duration = cls.range_check_interval(start, end, duration)
if start is not None and end is not None:
# <start>/<end>
startobject = cls._build_object(start)
endobject = cls._build_object(end)
return (startobject, endobject)
durationobject = cls._build_object(duration)
# Determine if datetime promotion is required
datetimerequired = (
duration.TnH is not None
or duration.TnM is not None
or duration.TnS is not None
or durationobject.seconds != 0
or durationobject.microseconds != 0
)
if end is not None:
# <duration>/<end>
endobject = cls._build_object(end)
# Range check
if type(end) is DateTuple and datetimerequired is True:
# <end> is a date, and <duration> requires datetime resolution
return (
endobject,
cls.build_datetime(end, TupleBuilder.build_time()) - durationobject,
)
return (endobject, endobject - durationobject)
# <start>/<duration>
startobject = cls._build_object(start)
# Range check
if type(start) is DateTuple and datetimerequired is True:
# <start> is a date, and <duration> requires datetime resolution
return (
startobject,
cls.build_datetime(start, TupleBuilder.build_time()) + durationobject,
)
return (startobject, startobject + durationobject)
@classmethod
def build_repeating_interval(cls, R=None, Rnn=None, interval=None):
startobject = None
endobject = None
R, Rnn, interval = cls.range_check_repeating_interval(R, Rnn, interval)
if interval.start is not None:
startobject = cls._build_object(interval.start)
if interval.end is not None:
endobject = cls._build_object(interval.end)
if interval.duration is not None:
durationobject = cls._build_object(interval.duration)
else:
durationobject = endobject - startobject
if R is True:
if startobject is not None:
return cls._date_generator_unbounded(startobject, durationobject)
return cls._date_generator_unbounded(endobject, -durationobject)
iterations = int(Rnn)
if startobject is not None:
return cls._date_generator(startobject, durationobject, iterations)
return cls._date_generator(endobject, -durationobject, iterations)
@classmethod
def build_timezone(cls, negative=None, Z=None, hh=None, mm=None, name=""):
negative, Z, hh, mm, name = cls.range_check_timezone(negative, Z, hh, mm, name)
if Z is True:
# Z -> UTC
return UTCOffset(name="UTC", minutes=0)
tzhour = int(hh)
if mm is not None:
tzminute = int(mm)
else:
tzminute = 0
if negative is True:
return UTCOffset(name=name, minutes=-(tzhour * 60 + tzminute))
return UTCOffset(name=name, minutes=tzhour * 60 + tzminute)
@classmethod
def range_check_duration(
cls,
PnY=None,
PnM=None,
PnW=None,
PnD=None,
TnH=None,
TnM=None,
TnS=None,
rangedict=None,
):
years = 0
months = 0
days = 0
weeks = 0
hours = 0
minutes = 0
seconds = 0
microseconds = 0
PnY, PnM, PnW, PnD, TnH, TnM, TnS = BaseTimeBuilder.range_check_duration(
PnY, PnM, PnW, PnD, TnH, TnM, TnS, rangedict=cls.DURATION_RANGE_DICT
)
if PnY is not None:
if type(PnY) is FractionalComponent:
years = PnY.principal
microseconds = PnY.microsecondremainder
else:
years = PnY
if years * DAYS_PER_YEAR > TIMEDELTA_MAX_DAYS:
raise YearOutOfBoundsError("Duration exceeds maximum timedelta size.")
if PnM is not None:
if type(PnM) is FractionalComponent:
months = PnM.principal
microseconds = PnM.microsecondremainder
else:
months = PnM
if months * DAYS_PER_MONTH > TIMEDELTA_MAX_DAYS:
raise MonthOutOfBoundsError("Duration exceeds maximum timedelta size.")
if PnW is not None:
if type(PnW) is FractionalComponent:
weeks = PnW.principal
microseconds = PnW.microsecondremainder
else:
weeks = PnW
if weeks * DAYS_PER_WEEK > TIMEDELTA_MAX_DAYS:
raise WeekOutOfBoundsError("Duration exceeds maximum timedelta size.")
if PnD is not None:
if type(PnD) is FractionalComponent:
days = PnD.principal
microseconds = PnD.microsecondremainder
else:
days = PnD
if days > TIMEDELTA_MAX_DAYS:
raise DayOutOfBoundsError("Duration exceeds maximum timedelta size.")
if TnH is not None:
if type(TnH) is FractionalComponent:
hours = TnH.principal
microseconds = TnH.microsecondremainder
else:
hours = TnH
if hours // HOURS_PER_DAY > TIMEDELTA_MAX_DAYS:
raise HoursOutOfBoundsError("Duration exceeds maximum timedelta size.")
if TnM is not None:
if type(TnM) is FractionalComponent:
minutes = TnM.principal
microseconds = TnM.microsecondremainder
else:
minutes = TnM
if minutes // MINUTES_PER_DAY > TIMEDELTA_MAX_DAYS:
raise MinutesOutOfBoundsError(
"Duration exceeds maximum timedelta size."
)
if TnS is not None:
if type(TnS) is FractionalComponent:
seconds = TnS.principal
microseconds = TnS.microsecondremainder
else:
seconds = TnS
if seconds // SECONDS_PER_DAY > TIMEDELTA_MAX_DAYS:
raise SecondsOutOfBoundsError(
"Duration exceeds maximum timedelta size."
)
(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
microseconds,
) = PythonTimeBuilder._distribute_microseconds(
microseconds,
(years, months, weeks, days, hours, minutes, seconds),
(
MICROSECONDS_PER_YEAR,
MICROSECONDS_PER_MONTH,
MICROSECONDS_PER_WEEK,
MICROSECONDS_PER_DAY,
MICROSECONDS_PER_HOUR,
MICROSECONDS_PER_MINUTE,
MICROSECONDS_PER_SECOND,
),
)
# Note that weeks can be handled without conversion to days
totaldays = years * DAYS_PER_YEAR + months * DAYS_PER_MONTH + days
# Check against timedelta limits
if (
totaldays
+ weeks * DAYS_PER_WEEK
+ hours // HOURS_PER_DAY
+ minutes // MINUTES_PER_DAY
+ seconds // SECONDS_PER_DAY
> TIMEDELTA_MAX_DAYS
):
raise DayOutOfBoundsError("Duration exceeds maximum timedelta size.")
return (
None,
None,
weeks,
totaldays,
hours,
minutes,
FractionalComponent(seconds, microseconds),
)
@classmethod
def range_check_interval(cls, start=None, end=None, duration=None):
# Handles concise format, range checks any potential durations
if start is not None and end is not None:
# <start>/<end>
# Handle concise format
if cls._is_interval_end_concise(end) is True:
end = cls._combine_concise_interval_tuples(start, end)
return (start, end, duration)
durationobject = cls._build_object(duration)
if end is not None:
# <duration>/<end>
endobject = cls._build_object(end)
# Range check
if type(end) is DateTuple:
enddatetime = cls.build_datetime(end, TupleBuilder.build_time())
if enddatetime - datetime.datetime.min < durationobject:
raise YearOutOfBoundsError("Interval end less than minimium date.")
else:
mindatetime = datetime.datetime.min
if end.time.tz is not None:
mindatetime = mindatetime.replace(tzinfo=endobject.tzinfo)
if endobject - mindatetime < durationobject:
raise YearOutOfBoundsError("Interval end less than minimium date.")
else:
# <start>/<duration>
startobject = cls._build_object(start)
# Range check
if type(start) is DateTuple:
startdatetime = cls.build_datetime(start, TupleBuilder.build_time())
if datetime.datetime.max - startdatetime < durationobject:
raise YearOutOfBoundsError(
"Interval end greater than maximum date."
)
else:
maxdatetime = datetime.datetime.max
if start.time.tz is not None:
maxdatetime = maxdatetime.replace(tzinfo=startobject.tzinfo)
if maxdatetime - startobject < durationobject:
raise YearOutOfBoundsError(
"Interval end greater than maximum date."
)
return (start, end, duration)
@staticmethod
def _build_week_date(isoyear, isoweek, isoday=None):
if isoday is None:
return PythonTimeBuilder._iso_year_start(isoyear) + datetime.timedelta(
weeks=isoweek - 1
)
return PythonTimeBuilder._iso_year_start(isoyear) + datetime.timedelta(
weeks=isoweek - 1, days=isoday - 1
)
@staticmethod
def _build_ordinal_date(isoyear, isoday):
# Day of year to a date
# https://stackoverflow.com/questions/2427555/python-question-year-and-day-of-year-to-date
builtdate = datetime.date(isoyear, 1, 1) + datetime.timedelta(days=isoday - 1)
return builtdate
@staticmethod
def _iso_year_start(isoyear):
# Given an ISO year, returns the equivalent of the start of the year
# on the Gregorian calendar (which is used by Python)
# Stolen from:
# http://stackoverflow.com/questions/304256/whats-the-best-way-to-find-the-inverse-of-datetime-isocalendar
# Determine the location of the 4th of January, the first week of
# the ISO year is the week containing the 4th of January
# http://en.wikipedia.org/wiki/ISO_week_date
fourth_jan = datetime.date(isoyear, 1, 4)
# Note the conversion from ISO day (1 - 7) and Python day (0 - 6)
delta = datetime.timedelta(days=fourth_jan.isoweekday() - 1)
# Return the start of the year
return fourth_jan - delta
@staticmethod
def _date_generator(startdate, timedelta, iterations):
currentdate = startdate
currentiteration = 0
while currentiteration < iterations:
yield currentdate
# Update the values
currentdate += timedelta
currentiteration += 1
@staticmethod
def _date_generator_unbounded(startdate, timedelta):
currentdate = startdate
while True:
yield currentdate
# Update the value
currentdate += timedelta
@staticmethod
def _distribute_microseconds(todistribute, recipients, reductions):
# Given a number of microseconds as int, a tuple of ints length n
# to distribute to, and a tuple of ints length n to divide todistribute
# by (from largest to smallest), returns a tuple of length n + 1, with
# todistribute divided across recipients using the reductions, with
# the final remainder returned as the final tuple member
results = []
remainder = todistribute
for index, reduction in enumerate(reductions):
additional, remainder = divmod(remainder, reduction)
results.append(recipients[index] + additional)
# Always return the remaining microseconds
results.append(remainder)
return tuple(results)

7
Lib/site-packages/aniso8601/builders/tests/__init__.py

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.

BIN
Lib/site-packages/aniso8601/builders/tests/__pycache__/__init__.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/builders/tests/__pycache__/test_init.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/builders/tests/__pycache__/test_python.cpython-37.pyc

838
Lib/site-packages/aniso8601/builders/tests/test_init.py

@ -0,0 +1,838 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import unittest
import aniso8601
from aniso8601.builders import (
BaseTimeBuilder,
DatetimeTuple,
DateTuple,
DurationTuple,
IntervalTuple,
RepeatingIntervalTuple,
TimeTuple,
TimezoneTuple,
TupleBuilder,
cast,
)
from aniso8601.exceptions import (
DayOutOfBoundsError,
HoursOutOfBoundsError,
ISOFormatError,
LeapSecondError,
MidnightBoundsError,
MinutesOutOfBoundsError,
MonthOutOfBoundsError,
SecondsOutOfBoundsError,
WeekOutOfBoundsError,
)
from aniso8601.tests.compat import mock
class LeapSecondSupportingTestBuilder(BaseTimeBuilder):
LEAP_SECONDS_SUPPORTED = True
class TestBuilderFunctions(unittest.TestCase):
def test_cast(self):
self.assertEqual(cast("1", int), 1)
self.assertEqual(cast("-2", int), -2)
self.assertEqual(cast("3", float), float(3))
self.assertEqual(cast("-4", float), float(-4))
self.assertEqual(cast("5.6", float), 5.6)
self.assertEqual(cast("-7.8", float), -7.8)
def test_cast_exception(self):
with self.assertRaises(ISOFormatError):
cast("asdf", int)
with self.assertRaises(ISOFormatError):
cast("asdf", float)
def test_cast_caughtexception(self):
def tester(value):
raise RuntimeError
with self.assertRaises(ISOFormatError):
cast("asdf", tester, caughtexceptions=(RuntimeError,))
def test_cast_thrownexception(self):
with self.assertRaises(RuntimeError):
cast("asdf", int, thrownexception=RuntimeError)
class TestBaseTimeBuilder(unittest.TestCase):
def test_build_date(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_date()
def test_build_time(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_time()
def test_build_datetime(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_datetime(None, None)
def test_build_duration(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_duration()
def test_build_interval(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_interval()
def test_build_repeating_interval(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_repeating_interval()
def test_build_timezone(self):
with self.assertRaises(NotImplementedError):
BaseTimeBuilder.build_timezone()
def test_range_check_date(self):
# Check the calendar for day ranges
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="0007", MM="02", DD="30")
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="0007", DDD="366")
with self.assertRaises(MonthOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="4333", MM="30", DD="30")
# 0 isn't a valid week number
with self.assertRaises(WeekOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="2003", Www="00")
# Week must not be larger than 53
with self.assertRaises(WeekOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="2004", Www="54")
# 0 isn't a valid day number
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="0")
# Day must not be larger than 7
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="2001", Www="02", D="8")
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="1981", DDD="000")
# Day must be 365, or 366, not larger
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="1234", DDD="000")
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="1234", DDD="367")
# https://bitbucket.org/nielsenb/aniso8601/issues/14/parsing-ordinal-dates-should-only-allow
with self.assertRaises(DayOutOfBoundsError):
BaseTimeBuilder.range_check_date(YYYY="1981", DDD="366")
# Make sure Nones pass through unmodified
self.assertEqual(
BaseTimeBuilder.range_check_date(rangedict={}),
(None, None, None, None, None, None),
)
def test_range_check_time(self):
# Leap seconds not supported
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
# https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly
with self.assertRaises(LeapSecondError):
BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60")
with self.assertRaises(SecondsOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="60")
with self.assertRaises(SecondsOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="00", mm="00", ss="61")
with self.assertRaises(MinutesOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="00", mm="61")
with self.assertRaises(MinutesOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="00", mm="60")
with self.assertRaises(MinutesOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="00", mm="60.1")
with self.assertRaises(HoursOutOfBoundsError):
BaseTimeBuilder.range_check_time(hh="25")
# Hour 24 can only represent midnight
with self.assertRaises(MidnightBoundsError):
BaseTimeBuilder.range_check_time(hh="24", mm="00", ss="01")
with self.assertRaises(MidnightBoundsError):
BaseTimeBuilder.range_check_time(hh="24", mm="00.1")
with self.assertRaises(MidnightBoundsError):
BaseTimeBuilder.range_check_time(hh="24", mm="01")
with self.assertRaises(MidnightBoundsError):
BaseTimeBuilder.range_check_time(hh="24.1")
# Leap seconds not supported
# https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
# https://bitbucket.org/nielsenb/aniso8601/issues/13/parsing-of-leap-second-gives-wildly
with self.assertRaises(LeapSecondError):
BaseTimeBuilder.range_check_time(hh="23", mm="59", ss="60")
# Make sure Nones pass through unmodified
self.assertEqual(
BaseTimeBuilder.range_check_time(rangedict={}), (None, None, None, None)
)
def test_range_check_time_leap_seconds_supported(self):
self.assertEqual(
LeapSecondSupportingTestBuilder.range_check_time(hh="23", mm="59", ss="60"),
(23, 59, 60, None),
)
with self.assertRaises(SecondsOutOfBoundsError):
LeapSecondSupportingTestBuilder.range_check_time(hh="01", mm="02", ss="60")
def test_range_check_duration(self):
self.assertEqual(
BaseTimeBuilder.range_check_duration(),
(None, None, None, None, None, None, None),
)
self.assertEqual(
BaseTimeBuilder.range_check_duration(rangedict={}),
(None, None, None, None, None, None, None),
)
def test_range_check_repeating_interval(self):
self.assertEqual(
BaseTimeBuilder.range_check_repeating_interval(), (None, None, None)
)
self.assertEqual(
BaseTimeBuilder.range_check_repeating_interval(rangedict={}),
(None, None, None),
)
def test_range_check_timezone(self):
self.assertEqual(
BaseTimeBuilder.range_check_timezone(), (None, None, None, None, "")
)
self.assertEqual(
BaseTimeBuilder.range_check_timezone(rangedict={}),
(None, None, None, None, ""),
)
def test_build_object(self):
datetest = (
DateTuple("1", "2", "3", "4", "5", "6"),
{"YYYY": "1", "MM": "2", "DD": "3", "Www": "4", "D": "5", "DDD": "6"},
)
timetest = (
TimeTuple("1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name")),
{
"hh": "1",
"mm": "2",
"ss": "3",
"tz": TimezoneTuple(False, False, "4", "5", "tz name"),
},
)
datetimetest = (
DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple(
"7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
),
),
(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple(
"7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
),
),
)
durationtest = (
DurationTuple("1", "2", "3", "4", "5", "6", "7"),
{
"PnY": "1",
"PnM": "2",
"PnW": "3",
"PnD": "4",
"TnH": "5",
"TnM": "6",
"TnS": "7",
},
)
intervaltests = (
(
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
{
"start": DateTuple("1", "2", "3", "4", "5", "6"),
"end": DateTuple("7", "8", "9", "10", "11", "12"),
"duration": None,
},
),
(
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
None,
DurationTuple("7", "8", "9", "10", "11", "12", "13"),
),
{
"start": DateTuple("1", "2", "3", "4", "5", "6"),
"end": None,
"duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"),
},
),
(
IntervalTuple(
None,
TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
),
DurationTuple("6", "7", "8", "9", "10", "11", "12"),
),
{
"start": None,
"end": TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
),
"duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"),
},
),
)
repeatingintervaltests = (
(
RepeatingIntervalTuple(
True,
None,
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
),
{
"R": True,
"Rnn": None,
"interval": IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
},
),
(
RepeatingIntervalTuple(
False,
"1",
IntervalTuple(
DatetimeTuple(
DateTuple("2", "3", "4", "5", "6", "7"),
TimeTuple("8", "9", "10", None),
),
DatetimeTuple(
DateTuple("11", "12", "13", "14", "15", "16"),
TimeTuple("17", "18", "19", None),
),
None,
),
),
{
"R": False,
"Rnn": "1",
"interval": IntervalTuple(
DatetimeTuple(
DateTuple("2", "3", "4", "5", "6", "7"),
TimeTuple("8", "9", "10", None),
),
DatetimeTuple(
DateTuple("11", "12", "13", "14", "15", "16"),
TimeTuple("17", "18", "19", None),
),
None,
),
},
),
)
timezonetest = (
TimezoneTuple(False, False, "1", "2", "+01:02"),
{"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"},
)
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_date"
) as mock_build:
mock_build.return_value = datetest[0]
result = BaseTimeBuilder._build_object(datetest[0])
self.assertEqual(result, datetest[0])
mock_build.assert_called_once_with(**datetest[1])
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_time"
) as mock_build:
mock_build.return_value = timetest[0]
result = BaseTimeBuilder._build_object(timetest[0])
self.assertEqual(result, timetest[0])
mock_build.assert_called_once_with(**timetest[1])
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_datetime"
) as mock_build:
mock_build.return_value = datetimetest[0]
result = BaseTimeBuilder._build_object(datetimetest[0])
self.assertEqual(result, datetimetest[0])
mock_build.assert_called_once_with(*datetimetest[1])
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_duration"
) as mock_build:
mock_build.return_value = durationtest[0]
result = BaseTimeBuilder._build_object(durationtest[0])
self.assertEqual(result, durationtest[0])
mock_build.assert_called_once_with(**durationtest[1])
for intervaltest in intervaltests:
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_interval"
) as mock_build:
mock_build.return_value = intervaltest[0]
result = BaseTimeBuilder._build_object(intervaltest[0])
self.assertEqual(result, intervaltest[0])
mock_build.assert_called_once_with(**intervaltest[1])
for repeatingintervaltest in repeatingintervaltests:
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_repeating_interval"
) as mock_build:
mock_build.return_value = repeatingintervaltest[0]
result = BaseTimeBuilder._build_object(repeatingintervaltest[0])
self.assertEqual(result, repeatingintervaltest[0])
mock_build.assert_called_once_with(**repeatingintervaltest[1])
with mock.patch.object(
aniso8601.builders.BaseTimeBuilder, "build_timezone"
) as mock_build:
mock_build.return_value = timezonetest[0]
result = BaseTimeBuilder._build_object(timezonetest[0])
self.assertEqual(result, timezonetest[0])
mock_build.assert_called_once_with(**timezonetest[1])
def test_is_interval_end_concise(self):
self.assertTrue(
BaseTimeBuilder._is_interval_end_concise(TimeTuple("1", "2", "3", None))
)
self.assertTrue(
BaseTimeBuilder._is_interval_end_concise(
DateTuple(None, "2", "3", "4", "5", "6")
)
)
self.assertTrue(
BaseTimeBuilder._is_interval_end_concise(
DatetimeTuple(
DateTuple(None, "2", "3", "4", "5", "6"),
TimeTuple("7", "8", "9", None),
)
)
)
self.assertFalse(
BaseTimeBuilder._is_interval_end_concise(
DateTuple("1", "2", "3", "4", "5", "6")
)
)
self.assertFalse(
BaseTimeBuilder._is_interval_end_concise(
DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple("7", "8", "9", None),
)
)
)
def test_combine_concise_interval_tuples(self):
testtuples = (
(
DateTuple("2020", "01", "01", None, None, None),
DateTuple(None, None, "02", None, None, None),
DateTuple("2020", "01", "02", None, None, None),
),
(
DateTuple("2008", "02", "15", None, None, None),
DateTuple(None, "03", "14", None, None, None),
DateTuple("2008", "03", "14", None, None, None),
),
(
DatetimeTuple(
DateTuple("2007", "12", "14", None, None, None),
TimeTuple("13", "30", None, None),
),
TimeTuple("15", "30", None, None),
DatetimeTuple(
DateTuple("2007", "12", "14", None, None, None),
TimeTuple("15", "30", None, None),
),
),
(
DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple("09", "00", None, None),
),
DatetimeTuple(
DateTuple(None, None, "15", None, None, None),
TimeTuple("17", "00", None, None),
),
DatetimeTuple(
DateTuple("2007", "11", "15", None, None, None),
TimeTuple("17", "00", None, None),
),
),
(
DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple("00", "00", None, None),
),
DatetimeTuple(
DateTuple(None, None, "16", None, None, None),
TimeTuple("00", "00", None, None),
),
DatetimeTuple(
DateTuple("2007", "11", "16", None, None, None),
TimeTuple("00", "00", None, None),
),
),
(
DatetimeTuple(
DateTuple("2007", "11", "13", None, None, None),
TimeTuple(
"09", "00", None, TimezoneTuple(False, True, None, None, "Z")
),
),
DatetimeTuple(
DateTuple(None, None, "15", None, None, None),
TimeTuple("17", "00", None, None),
),
DatetimeTuple(
DateTuple("2007", "11", "15", None, None, None),
TimeTuple(
"17", "00", None, TimezoneTuple(False, True, None, None, "Z")
),
),
),
)
for testtuple in testtuples:
result = BaseTimeBuilder._combine_concise_interval_tuples(
testtuple[0], testtuple[1]
)
self.assertEqual(result, testtuple[2])
class TestTupleBuilder(unittest.TestCase):
def test_build_date(self):
datetuple = TupleBuilder.build_date()
self.assertEqual(datetuple, DateTuple(None, None, None, None, None, None))
datetuple = TupleBuilder.build_date(
YYYY="1", MM="2", DD="3", Www="4", D="5", DDD="6"
)
self.assertEqual(datetuple, DateTuple("1", "2", "3", "4", "5", "6"))
def test_build_time(self):
testtuples = (
({}, TimeTuple(None, None, None, None)),
(
{"hh": "1", "mm": "2", "ss": "3", "tz": None},
TimeTuple("1", "2", "3", None),
),
(
{
"hh": "1",
"mm": "2",
"ss": "3",
"tz": TimezoneTuple(False, False, "4", "5", "tz name"),
},
TimeTuple(
"1", "2", "3", TimezoneTuple(False, False, "4", "5", "tz name")
),
),
)
for testtuple in testtuples:
self.assertEqual(TupleBuilder.build_time(**testtuple[0]), testtuple[1])
def test_build_datetime(self):
testtuples = (
(
{
"date": DateTuple("1", "2", "3", "4", "5", "6"),
"time": TimeTuple("7", "8", "9", None),
},
DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple("7", "8", "9", None),
),
),
(
{
"date": DateTuple("1", "2", "3", "4", "5", "6"),
"time": TimeTuple(
"7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
),
},
DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple(
"7", "8", "9", TimezoneTuple(True, False, "10", "11", "tz name")
),
),
),
)
for testtuple in testtuples:
self.assertEqual(TupleBuilder.build_datetime(**testtuple[0]), testtuple[1])
def test_build_duration(self):
testtuples = (
({}, DurationTuple(None, None, None, None, None, None, None)),
(
{
"PnY": "1",
"PnM": "2",
"PnW": "3",
"PnD": "4",
"TnH": "5",
"TnM": "6",
"TnS": "7",
},
DurationTuple("1", "2", "3", "4", "5", "6", "7"),
),
)
for testtuple in testtuples:
self.assertEqual(TupleBuilder.build_duration(**testtuple[0]), testtuple[1])
def test_build_interval(self):
testtuples = (
({}, IntervalTuple(None, None, None)),
(
{
"start": DateTuple("1", "2", "3", "4", "5", "6"),
"end": DateTuple("7", "8", "9", "10", "11", "12"),
},
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
),
(
{
"start": TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name")
),
"end": TimeTuple(
"4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name")
),
},
IntervalTuple(
TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "7", "8", "tz name")
),
TimeTuple(
"4", "5", "6", TimezoneTuple(False, False, "9", "10", "tz name")
),
None,
),
),
(
{
"start": DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple(
"7",
"8",
"9",
TimezoneTuple(True, False, "10", "11", "tz name"),
),
),
"end": DatetimeTuple(
DateTuple("12", "13", "14", "15", "16", "17"),
TimeTuple(
"18",
"19",
"20",
TimezoneTuple(False, False, "21", "22", "tz name"),
),
),
},
IntervalTuple(
DatetimeTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
TimeTuple(
"7",
"8",
"9",
TimezoneTuple(True, False, "10", "11", "tz name"),
),
),
DatetimeTuple(
DateTuple("12", "13", "14", "15", "16", "17"),
TimeTuple(
"18",
"19",
"20",
TimezoneTuple(False, False, "21", "22", "tz name"),
),
),
None,
),
),
(
{
"start": DateTuple("1", "2", "3", "4", "5", "6"),
"end": None,
"duration": DurationTuple("7", "8", "9", "10", "11", "12", "13"),
},
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
None,
DurationTuple("7", "8", "9", "10", "11", "12", "13"),
),
),
(
{
"start": None,
"end": TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
),
"duration": DurationTuple("6", "7", "8", "9", "10", "11", "12"),
},
IntervalTuple(
None,
TimeTuple(
"1", "2", "3", TimezoneTuple(True, False, "4", "5", "tz name")
),
DurationTuple("6", "7", "8", "9", "10", "11", "12"),
),
),
)
for testtuple in testtuples:
self.assertEqual(TupleBuilder.build_interval(**testtuple[0]), testtuple[1])
def test_build_repeating_interval(self):
testtuples = (
({}, RepeatingIntervalTuple(None, None, None)),
(
{
"R": True,
"interval": IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
},
RepeatingIntervalTuple(
True,
None,
IntervalTuple(
DateTuple("1", "2", "3", "4", "5", "6"),
DateTuple("7", "8", "9", "10", "11", "12"),
None,
),
),
),
(
{
"R": False,
"Rnn": "1",
"interval": IntervalTuple(
DatetimeTuple(
DateTuple("2", "3", "4", "5", "6", "7"),
TimeTuple("8", "9", "10", None),
),
DatetimeTuple(
DateTuple("11", "12", "13", "14", "15", "16"),
TimeTuple("17", "18", "19", None),
),
None,
),
},
RepeatingIntervalTuple(
False,
"1",
IntervalTuple(
DatetimeTuple(
DateTuple("2", "3", "4", "5", "6", "7"),
TimeTuple("8", "9", "10", None),
),
DatetimeTuple(
DateTuple("11", "12", "13", "14", "15", "16"),
TimeTuple("17", "18", "19", None),
),
None,
),
),
),
)
for testtuple in testtuples:
result = TupleBuilder.build_repeating_interval(**testtuple[0])
self.assertEqual(result, testtuple[1])
def test_build_timezone(self):
testtuples = (
({}, TimezoneTuple(None, None, None, None, "")),
(
{"negative": False, "Z": True, "name": "UTC"},
TimezoneTuple(False, True, None, None, "UTC"),
),
(
{"negative": False, "Z": False, "hh": "1", "mm": "2", "name": "+01:02"},
TimezoneTuple(False, False, "1", "2", "+01:02"),
),
(
{"negative": True, "Z": False, "hh": "1", "mm": "2", "name": "-01:02"},
TimezoneTuple(True, False, "1", "2", "-01:02"),
),
)
for testtuple in testtuples:
result = TupleBuilder.build_timezone(**testtuple[0])
self.assertEqual(result, testtuple[1])

1710
Lib/site-packages/aniso8601/builders/tests/test_python.py
File diff suppressed because it is too large
View File

24
Lib/site-packages/aniso8601/compat.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import sys
PY2 = sys.version_info[0] == 2
if PY2: # pragma: no cover
range = xrange # pylint: disable=undefined-variable
else:
range = range
def is_string(tocheck):
# pylint: disable=undefined-variable
if PY2: # pragma: no cover
return isinstance(tocheck, str) or isinstance(tocheck, unicode)
return isinstance(tocheck, str)

161
Lib/site-packages/aniso8601/date.py

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
from aniso8601.builders import TupleBuilder
from aniso8601.builders.python import PythonTimeBuilder
from aniso8601.compat import is_string
from aniso8601.exceptions import ISOFormatError
from aniso8601.resolution import DateResolution
def get_date_resolution(isodatestr):
# Valid string formats are:
#
# Y[YYY]
# YYYY-MM-DD
# YYYYMMDD
# YYYY-MM
# YYYY-Www
# YYYYWww
# YYYY-Www-D
# YYYYWwwD
# YYYY-DDD
# YYYYDDD
isodatetuple = parse_date(isodatestr, builder=TupleBuilder)
if isodatetuple.DDD is not None:
# YYYY-DDD
# YYYYDDD
return DateResolution.Ordinal
if isodatetuple.D is not None:
# YYYY-Www-D
# YYYYWwwD
return DateResolution.Weekday
if isodatetuple.Www is not None:
# YYYY-Www
# YYYYWww
return DateResolution.Week
if isodatetuple.DD is not None:
# YYYY-MM-DD
# YYYYMMDD
return DateResolution.Day
if isodatetuple.MM is not None:
# YYYY-MM
return DateResolution.Month
# Y[YYY]
return DateResolution.Year
def parse_date(isodatestr, builder=PythonTimeBuilder):
# Given a string in any ISO 8601 date format, return a datetime.date
# object that corresponds to the given date. Valid string formats are:
#
# Y[YYY]
# YYYY-MM-DD
# YYYYMMDD
# YYYY-MM
# YYYY-Www
# YYYYWww
# YYYY-Www-D
# YYYYWwwD
# YYYY-DDD
# YYYYDDD
if is_string(isodatestr) is False:
raise ValueError("Date must be string.")
if isodatestr.startswith("+") or isodatestr.startswith("-"):
raise NotImplementedError(
"ISO 8601 extended year representation " "not supported."
)
if len(isodatestr) == 0 or isodatestr.count("-") > 2:
raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr))
yearstr = None
monthstr = None
daystr = None
weekstr = None
weekdaystr = None
ordinaldaystr = None
if len(isodatestr) <= 4:
# Y[YYY]
yearstr = isodatestr
elif "W" in isodatestr:
if len(isodatestr) == 10:
# YYYY-Www-D
yearstr = isodatestr[0:4]
weekstr = isodatestr[6:8]
weekdaystr = isodatestr[9]
elif len(isodatestr) == 8:
if "-" in isodatestr:
# YYYY-Www
yearstr = isodatestr[0:4]
weekstr = isodatestr[6:]
else:
# YYYYWwwD
yearstr = isodatestr[0:4]
weekstr = isodatestr[5:7]
weekdaystr = isodatestr[7]
elif len(isodatestr) == 7:
# YYYYWww
yearstr = isodatestr[0:4]
weekstr = isodatestr[5:]
elif len(isodatestr) == 7:
if "-" in isodatestr:
# YYYY-MM
yearstr = isodatestr[0:4]
monthstr = isodatestr[5:]
else:
# YYYYDDD
yearstr = isodatestr[0:4]
ordinaldaystr = isodatestr[4:]
elif len(isodatestr) == 8:
if "-" in isodatestr:
# YYYY-DDD
yearstr = isodatestr[0:4]
ordinaldaystr = isodatestr[5:]
else:
# YYYYMMDD
yearstr = isodatestr[0:4]
monthstr = isodatestr[4:6]
daystr = isodatestr[6:]
elif len(isodatestr) == 10:
# YYYY-MM-DD
yearstr = isodatestr[0:4]
monthstr = isodatestr[5:7]
daystr = isodatestr[8:]
else:
raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr))
hascomponent = False
for componentstr in [yearstr, monthstr, daystr, weekstr, weekdaystr, ordinaldaystr]:
if componentstr is not None:
hascomponent = True
if componentstr.isdigit() is False:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 date.'.format(isodatestr)
)
if hascomponent is False:
raise ISOFormatError('"{0}" is not a valid ISO 8601 date.'.format(isodatestr))
return builder.build_date(
YYYY=yearstr,
MM=monthstr,
DD=daystr,
Www=weekstr,
D=weekdaystr,
DDD=ordinaldaystr,
)

12
Lib/site-packages/aniso8601/decimalfraction.py

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
def normalize(value):
"""Returns the string with decimal separators normalized."""
return value.replace(",", ".")

291
Lib/site-packages/aniso8601/duration.py

@ -0,0 +1,291 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
from aniso8601 import compat
from aniso8601.builders import TupleBuilder
from aniso8601.builders.python import PythonTimeBuilder
from aniso8601.date import parse_date
from aniso8601.decimalfraction import normalize
from aniso8601.exceptions import ISOFormatError
from aniso8601.resolution import DurationResolution
from aniso8601.time import parse_time
def get_duration_resolution(isodurationstr):
# Valid string formats are:
#
# PnYnMnDTnHnMnS (or any reduced precision equivalent)
# PnW
# P<date>T<time>
isodurationtuple = parse_duration(isodurationstr, builder=TupleBuilder)
if isodurationtuple.TnS is not None:
return DurationResolution.Seconds
if isodurationtuple.TnM is not None:
return DurationResolution.Minutes
if isodurationtuple.TnH is not None:
return DurationResolution.Hours
if isodurationtuple.PnD is not None:
return DurationResolution.Days
if isodurationtuple.PnW is not None:
return DurationResolution.Weeks
if isodurationtuple.PnM is not None:
return DurationResolution.Months
return DurationResolution.Years
def parse_duration(isodurationstr, builder=PythonTimeBuilder):
# Given a string representing an ISO 8601 duration, return a
# a duration built by the given builder. Valid formats are:
#
# PnYnMnDTnHnMnS (or any reduced precision equivalent)
# PnW
# P<date>T<time>
if compat.is_string(isodurationstr) is False:
raise ValueError("Duration must be string.")
if len(isodurationstr) == 0:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
if isodurationstr[0] != "P":
raise ISOFormatError("ISO 8601 duration must start with a P.")
# If Y, M, D, H, S, or W are in the string,
# assume it is a specified duration
if _has_any_component(isodurationstr, ["Y", "M", "D", "H", "S", "W"]) is True:
parseresult = _parse_duration_prescribed(isodurationstr)
return builder.build_duration(**parseresult)
if isodurationstr.find("T") != -1:
parseresult = _parse_duration_combined(isodurationstr)
return builder.build_duration(**parseresult)
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
def _parse_duration_prescribed(isodurationstr):
# durationstr can be of the form PnYnMnDTnHnMnS or PnW
# Make sure the end character is valid
# https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
if isodurationstr[-1] not in ["Y", "M", "D", "H", "S", "W"]:
raise ISOFormatError("ISO 8601 duration must end with a valid " "character.")
# Make sure only the lowest order element has decimal precision
durationstr = normalize(isodurationstr)
if durationstr.count(".") > 1:
raise ISOFormatError(
"ISO 8601 allows only lowest order element to " "have a decimal fraction."
)
seperatoridx = durationstr.find(".")
if seperatoridx != -1:
remaining = durationstr[seperatoridx + 1 : -1]
# There should only ever be 1 letter after a decimal if there is more
# then one, the string is invalid
if remaining.isdigit() is False:
raise ISOFormatError(
"ISO 8601 duration must end with " "a single valid character."
)
# Do not allow W in combination with other designators
# https://bitbucket.org/nielsenb/aniso8601/issues/2/week-designators-should-not-be-combinable
if (
durationstr.find("W") != -1
and _has_any_component(durationstr, ["Y", "M", "D", "H", "S"]) is True
):
raise ISOFormatError(
"ISO 8601 week designators may not be combined "
"with other time designators."
)
# Parse the elements of the duration
if durationstr.find("T") == -1:
return _parse_duration_prescribed_notime(durationstr)
return _parse_duration_prescribed_time(durationstr)
def _parse_duration_prescribed_notime(isodurationstr):
# durationstr can be of the form PnYnMnD or PnW
durationstr = normalize(isodurationstr)
yearstr = None
monthstr = None
daystr = None
weekstr = None
weekidx = durationstr.find("W")
yearidx = durationstr.find("Y")
monthidx = durationstr.find("M")
dayidx = durationstr.find("D")
if weekidx != -1:
weekstr = durationstr[1:-1]
elif yearidx != -1 and monthidx != -1 and dayidx != -1:
yearstr = durationstr[1:yearidx]
monthstr = durationstr[yearidx + 1 : monthidx]
daystr = durationstr[monthidx + 1 : -1]
elif yearidx != -1 and monthidx != -1:
yearstr = durationstr[1:yearidx]
monthstr = durationstr[yearidx + 1 : monthidx]
elif yearidx != -1 and dayidx != -1:
yearstr = durationstr[1:yearidx]
daystr = durationstr[yearidx + 1 : dayidx]
elif monthidx != -1 and dayidx != -1:
monthstr = durationstr[1:monthidx]
daystr = durationstr[monthidx + 1 : -1]
elif yearidx != -1:
yearstr = durationstr[1:-1]
elif monthidx != -1:
monthstr = durationstr[1:-1]
elif dayidx != -1:
daystr = durationstr[1:-1]
else:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
for componentstr in [yearstr, monthstr, daystr, weekstr]:
if componentstr is not None:
if "." in componentstr:
intstr, fractionalstr = componentstr.split(".", 1)
if intstr.isdigit() is False:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
else:
if componentstr.isdigit() is False:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
return {"PnY": yearstr, "PnM": monthstr, "PnW": weekstr, "PnD": daystr}
def _parse_duration_prescribed_time(isodurationstr):
# durationstr can be of the form PnYnMnDTnHnMnS
timeidx = isodurationstr.find("T")
datestr = isodurationstr[:timeidx]
timestr = normalize(isodurationstr[timeidx + 1 :])
hourstr = None
minutestr = None
secondstr = None
houridx = timestr.find("H")
minuteidx = timestr.find("M")
secondidx = timestr.find("S")
if houridx != -1 and minuteidx != -1 and secondidx != -1:
hourstr = timestr[0:houridx]
minutestr = timestr[houridx + 1 : minuteidx]
secondstr = timestr[minuteidx + 1 : -1]
elif houridx != -1 and minuteidx != -1:
hourstr = timestr[0:houridx]
minutestr = timestr[houridx + 1 : minuteidx]
elif houridx != -1 and secondidx != -1:
hourstr = timestr[0:houridx]
secondstr = timestr[houridx + 1 : -1]
elif minuteidx != -1 and secondidx != -1:
minutestr = timestr[0:minuteidx]
secondstr = timestr[minuteidx + 1 : -1]
elif houridx != -1:
hourstr = timestr[0:-1]
elif minuteidx != -1:
minutestr = timestr[0:-1]
elif secondidx != -1:
secondstr = timestr[0:-1]
else:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
for componentstr in [hourstr, minutestr, secondstr]:
if componentstr is not None:
if "." in componentstr:
intstr, fractionalstr = componentstr.split(".", 1)
if intstr.isdigit() is False:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
else:
if componentstr.isdigit() is False:
raise ISOFormatError(
'"{0}" is not a valid ISO 8601 duration.'.format(isodurationstr)
)
# Parse any date components
durationdict = {"PnY": None, "PnM": None, "PnW": None, "PnD": None}
if len(datestr) > 1:
durationdict = _parse_duration_prescribed_notime(datestr)
durationdict.update({"TnH": hourstr, "TnM": minutestr, "TnS": secondstr})
return durationdict
def _parse_duration_combined(durationstr):
# Period of the form P<date>T<time>
# Split the string in to its component parts
datepart, timepart = durationstr[1:].split("T", 1) # We skip the 'P'
datevalue = parse_date(datepart, builder=TupleBuilder)
timevalue = parse_time(timepart, builder=TupleBuilder)
return {
"PnY": datevalue.YYYY,
"PnM": datevalue.MM,
"PnD": datevalue.DD,
"TnH": timevalue.hh,
"TnM": timevalue.mm,
"TnS": timevalue.ss,
}
def _has_any_component(durationstr, components):
# Given a duration string, and a list of components, returns True
# if any of the listed components are present, False otherwise.
#
# For instance:
# durationstr = 'P1Y'
# components = ['Y', 'M']
#
# returns True
#
# durationstr = 'P1Y'
# components = ['M', 'D']
#
# returns False
for component in components:
if durationstr.find(component) != -1:
return True
return False

51
Lib/site-packages/aniso8601/exceptions.py

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
class ISOFormatError(ValueError):
"""Raised when ISO 8601 string fails a format check."""
class RangeCheckError(ValueError):
"""Parent type of range check errors."""
class YearOutOfBoundsError(RangeCheckError):
"""Raised when year exceeds limits."""
class MonthOutOfBoundsError(RangeCheckError):
"""Raised when month is outside of 1..12."""
class WeekOutOfBoundsError(RangeCheckError):
"""Raised when week exceeds a year."""
class DayOutOfBoundsError(RangeCheckError):
"""Raised when day is outside of 1..365, 1..366 for leap year."""
class HoursOutOfBoundsError(RangeCheckError):
"""Raise when parsed hours are greater than 24."""
class MinutesOutOfBoundsError(RangeCheckError):
"""Raise when parsed seconds are greater than 60."""
class SecondsOutOfBoundsError(RangeCheckError):
"""Raise when parsed seconds are greater than 60."""
class MidnightBoundsError(RangeCheckError):
"""Raise when parsed time has an hour of 24 but is not midnight."""
class LeapSecondError(RangeCheckError):
"""Raised when attempting to parse a leap second"""

350
Lib/site-packages/aniso8601/interval.py

@ -0,0 +1,350 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
from aniso8601.builders import DatetimeTuple, DateTuple, TupleBuilder
from aniso8601.builders.python import PythonTimeBuilder
from aniso8601.compat import is_string
from aniso8601.date import parse_date
from aniso8601.duration import parse_duration
from aniso8601.exceptions import ISOFormatError
from aniso8601.resolution import IntervalResolution
from aniso8601.time import parse_datetime, parse_time
def get_interval_resolution(
isointervalstr, intervaldelimiter="/", datetimedelimiter="T"
):
isointervaltuple = parse_interval(
isointervalstr,
intervaldelimiter=intervaldelimiter,
datetimedelimiter=datetimedelimiter,
builder=TupleBuilder,
)
return _get_interval_resolution(isointervaltuple)
def get_repeating_interval_resolution(
isointervalstr, intervaldelimiter="/", datetimedelimiter="T"
):
repeatingintervaltuple = parse_repeating_interval(
isointervalstr,
intervaldelimiter=intervaldelimiter,
datetimedelimiter=datetimedelimiter,
builder=TupleBuilder,
)
return _get_interval_resolution(repeatingintervaltuple.interval)
def _get_interval_resolution(intervaltuple):
if intervaltuple.start is not None and intervaltuple.end is not None:
return max(
_get_interval_component_resolution(intervaltuple.start),
_get_interval_component_resolution(intervaltuple.end),
)
if intervaltuple.start is not None and intervaltuple.duration is not None:
return max(
_get_interval_component_resolution(intervaltuple.start),
_get_interval_component_resolution(intervaltuple.duration),
)
return max(
_get_interval_component_resolution(intervaltuple.end),
_get_interval_component_resolution(intervaltuple.duration),
)
def _get_interval_component_resolution(componenttuple):
if type(componenttuple) is DateTuple:
if componenttuple.DDD is not None:
# YYYY-DDD
# YYYYDDD
return IntervalResolution.Ordinal
if componenttuple.D is not None:
# YYYY-Www-D
# YYYYWwwD
return IntervalResolution.Weekday
if componenttuple.Www is not None:
# YYYY-Www
# YYYYWww
return IntervalResolution.Week
if componenttuple.DD is not None:
# YYYY-MM-DD
# YYYYMMDD
return IntervalResolution.Day
if componenttuple.MM is not None:
# YYYY-MM
return IntervalResolution.Month
# Y[YYY]
return IntervalResolution.Year
elif type(componenttuple) is DatetimeTuple:
# Datetime
if componenttuple.time.ss is not None:
return IntervalResolution.Seconds
if componenttuple.time.mm is not None:
return IntervalResolution.Minutes
return IntervalResolution.Hours
# Duration
if componenttuple.TnS is not None:
return IntervalResolution.Seconds
if componenttuple.TnM is not None:
return IntervalResolution.Minutes
if componenttuple.TnH is not None:
return IntervalResolution.Hours
if componenttuple.PnD is not None:
return IntervalResolution.Day
if componenttuple.PnW is not None:
return IntervalResolution.Week
if componenttuple.PnM is not None:
return IntervalResolution.Month
return IntervalResolution.Year
def parse_interval(
isointervalstr,
intervaldelimiter="/",
datetimedelimiter="T",
builder=PythonTimeBuilder,
):
# Given a string representing an ISO 8601 interval, return an
# interval built by the given builder. Valid formats are:
#
# <start>/<end>
# <start>/<duration>
# <duration>/<end>
#
# The <start> and <end> values can represent dates, or datetimes,
# not times.
#
# The format:
#
# <duration>
#
# Is expressly not supported as there is no way to provide the additional
# required context.
if is_string(isointervalstr) is False:
raise ValueError("Interval must be string.")
if len(isointervalstr) == 0:
raise ISOFormatError("Interval string is empty.")
if isointervalstr[0] == "R":
raise ISOFormatError(
"ISO 8601 repeating intervals must be parsed "
"with parse_repeating_interval."
)
intervaldelimitercount = isointervalstr.count(intervaldelimiter)
if intervaldelimitercount == 0:
raise ISOFormatError(
'Interval delimiter "{0}" is not in interval '
'string "{1}".'.format(intervaldelimiter, isointervalstr)
)
if intervaldelimitercount > 1:
raise ISOFormatError(
"{0} is not a valid ISO 8601 interval".format(isointervalstr)
)
return _parse_interval(
isointervalstr, builder, intervaldelimiter, datetimedelimiter
)
def parse_repeating_interval(
isointervalstr,
intervaldelimiter="/",
datetimedelimiter="T",
builder=PythonTimeBuilder,
):
# Given a string representing an ISO 8601 interval repeating, return an
# interval built by the given builder. Valid formats are:
#
# Rnn/<interval>
# R/<interval>
if not isinstance(isointervalstr, str):
raise ValueError("Interval must be string.")
if len(isointervalstr) == 0:
raise ISOFormatError("Repeating interval string is empty.")
if isointervalstr[0] != "R":
raise ISOFormatError("ISO 8601 repeating interval must start " "with an R.")
if intervaldelimiter not in isointervalstr:
raise ISOFormatError(
'Interval delimiter "{0}" is not in interval '
'string "{1}".'.format(intervaldelimiter, isointervalstr)
)
# Parse the number of iterations
iterationpart, intervalpart = isointervalstr.split(intervaldelimiter, 1)
if len(iterationpart) > 1:
R = False
Rnn = iterationpart[1:]
else:
R = True
Rnn = None
interval = _parse_interval(
intervalpart, TupleBuilder, intervaldelimiter, datetimedelimiter
)
return builder.build_repeating_interval(R=R, Rnn=Rnn, interval=interval)
def _parse_interval(
isointervalstr, builder, intervaldelimiter="/", datetimedelimiter="T"
):
# Returns a tuple containing the start of the interval, the end of the
# interval, and or the interval duration
firstpart, secondpart = isointervalstr.split(intervaldelimiter)
if len(firstpart) == 0 or len(secondpart) == 0:
raise ISOFormatError(
"{0} is not a valid ISO 8601 interval".format(isointervalstr)
)
if firstpart[0] == "P":
# <duration>/<end>
# Notice that these are not returned 'in order' (earlier to later), this
# is to maintain consistency with parsing <start>/<end> durations, as
# well as making repeating interval code cleaner. Users who desire
# durations to be in order can use the 'sorted' operator.
duration = parse_duration(firstpart, builder=TupleBuilder)
# We need to figure out if <end> is a date, or a datetime
if secondpart.find(datetimedelimiter) != -1:
# <end> is a datetime
endtuple = parse_datetime(
secondpart, delimiter=datetimedelimiter, builder=TupleBuilder
)
else:
endtuple = parse_date(secondpart, builder=TupleBuilder)
return builder.build_interval(end=endtuple, duration=duration)
elif secondpart[0] == "P":
# <start>/<duration>
# We need to figure out if <start> is a date, or a datetime
duration = parse_duration(secondpart, builder=TupleBuilder)
if firstpart.find(datetimedelimiter) != -1:
# <start> is a datetime
starttuple = parse_datetime(
firstpart, delimiter=datetimedelimiter, builder=TupleBuilder
)
else:
# <start> must just be a date
starttuple = parse_date(firstpart, builder=TupleBuilder)
return builder.build_interval(start=starttuple, duration=duration)
# <start>/<end>
if firstpart.find(datetimedelimiter) != -1:
# Both parts are datetimes
starttuple = parse_datetime(
firstpart, delimiter=datetimedelimiter, builder=TupleBuilder
)
else:
starttuple = parse_date(firstpart, builder=TupleBuilder)
endtuple = _parse_interval_end(secondpart, starttuple, datetimedelimiter)
return builder.build_interval(start=starttuple, end=endtuple)
def _parse_interval_end(endstr, starttuple, datetimedelimiter):
datestr = None
timestr = None
monthstr = None
daystr = None
concise = False
if type(starttuple) is DateTuple:
startdatetuple = starttuple
else:
# Start is a datetime
startdatetuple = starttuple.date
if datetimedelimiter in endstr:
datestr, timestr = endstr.split(datetimedelimiter, 1)
elif ":" in endstr:
timestr = endstr
else:
datestr = endstr
if timestr is not None:
endtimetuple = parse_time(timestr, builder=TupleBuilder)
# End is just a time
if datestr is None:
return endtimetuple
# Handle backwards concise representation
if datestr.count("-") == 1:
monthstr, daystr = datestr.split("-")
concise = True
elif len(datestr) <= 2:
daystr = datestr
concise = True
elif len(datestr) <= 4:
monthstr = datestr[0:2]
daystr = datestr[2:]
concise = True
if concise is True:
concisedatestr = startdatetuple.YYYY
# Separators required because concise elements may be missing digits
if monthstr is not None:
concisedatestr += "-" + monthstr
elif startdatetuple.MM is not None:
concisedatestr += "-" + startdatetuple.MM
concisedatestr += "-" + daystr
enddatetuple = parse_date(concisedatestr, builder=TupleBuilder)
# Clear unsupplied components
if monthstr is None:
enddatetuple = TupleBuilder.build_date(DD=enddatetuple.DD)
else:
# Year not provided
enddatetuple = TupleBuilder.build_date(
MM=enddatetuple.MM, DD=enddatetuple.DD
)
else:
enddatetuple = parse_date(datestr, builder=TupleBuilder)
if timestr is None:
return enddatetuple
return TupleBuilder.build_datetime(enddatetuple, endtimetuple)

27
Lib/site-packages/aniso8601/resolution.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
from aniso8601 import compat
class DateResolution(object):
Year, Month, Week, Weekday, Day, Ordinal = list(compat.range(6))
class DurationResolution(object):
Years, Months, Weeks, Days, Hours, Minutes, Seconds = list(compat.range(7))
class IntervalResolution(object):
Year, Month, Week, Weekday, Day, Ordinal, Hours, Minutes, Seconds = list(
compat.range(9)
)
class TimeResolution(object):
Seconds, Minutes, Hours = list(compat.range(3))

7
Lib/site-packages/aniso8601/tests/__init__.py

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.

BIN
Lib/site-packages/aniso8601/tests/__pycache__/__init__.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/compat.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_compat.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_date.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_decimalfraction.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_duration.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_init.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_interval.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_time.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_timezone.cpython-37.pyc

BIN
Lib/site-packages/aniso8601/tests/__pycache__/test_utcoffset.cpython-37.pyc

16
Lib/site-packages/aniso8601/tests/compat.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import sys
PY2 = sys.version_info[0] == 2
if PY2:
import mock # pylint: disable=import-error
else:
from unittest import mock

27
Lib/site-packages/aniso8601/tests/test_compat.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license. See the LICENSE file for details.
import unittest
from aniso8601.compat import PY2, is_string
class TestCompatFunctions(unittest.TestCase):
def test_is_string(self):
self.assertTrue(is_string("asdf"))
self.assertTrue(is_string(""))
# pylint: disable=undefined-variable
if PY2 is True:
self.assertTrue(is_string(unicode("asdf")))
self.assertFalse(is_string(None))
self.assertFalse(is_string(123))
self.assertFalse(is_string(4.56))
self.assertFalse(is_string([]))
self.assertFalse(is_string({}))

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save